Move the variable-bag contract (PlaceholderMap, MissingPlaceholderFn,
DefaultMissingMarker) up to the pkg/docforge root (placeholder.go) — it is
format-neutral, consumed by the resolver layer and any future exporter.
The {{key}} substitution grammar (placeholderRegex, PUA preview sentinels,
replacePlaceholders) stays in pkg/docforge/docx: it is the .docx renderer's
own machinery, not a root concern.
New at the root (vars.go):
- VariableResolver{Namespace() string; Populate(bag PlaceholderMap)} —
a PUSH interface, deliberately not pull Resolve(key): some namespaces
emit a data-dependent key set (parties.claimant.0.name, .1.name, … one
per party) that a fixed key-by-key pull can't enumerate.
- ResolverSet + BuildBag() — composes resolvers into one bag, replacing
the hard-coded addFooVars-then-addBarVars sequencing in Build.
paliad side (submission_vars_resolvers.go): seven resolver types wrap the
UNCHANGED addXxxVars push-builders (firm/today/user/procedural_event/
project/parties/deadline), each capturing the entity it needs. The builder
bodies are byte-for-byte untouched, so the bag is identical by
construction; SubmissionVarsService.Build now wires the applicable
resolvers and calls ResolverSet.BuildBag(). Resolvers stay in paliad
because they read paliad's domain model; a second docforge consumer plugs
its own resolvers into a ResolverSet the same way.
Keys()/Catalogue() (the static key list that will data-drive the authoring
palette + kill the hardcoded VARIABLE_GROUPS in submission-draft.ts) is
deferred to the UI slice that consumes it, sourced from the frontend's
existing labels — building it now, ahead of its consumer, would be
speculative (PRD §4 B3 principle).
Verification: go build ./... clean, go vet clean, full module test green.
Alias-parity (procedural_event ≡ rule) and party-form tests pass unchanged
= bag byte-identical.
m/paliad#157
88 lines
3.3 KiB
Go
88 lines
3.3 KiB
Go
package services
|
|
|
|
// Variable resolvers — the paliad-side implementations of
|
|
// docforge.VariableResolver (t-paliad-349 slice 3). Each wraps one of the
|
|
// addXxxVars push-builders, capturing the entity it needs, so the proven
|
|
// builder bodies stay byte-for-byte unchanged while the composition moves
|
|
// behind the docforge.ResolverSet seam. SubmissionVarsService.Build wires
|
|
// the applicable resolvers and calls ResolverSet.BuildBag().
|
|
//
|
|
// These live in paliad (not docforge) because they read paliad's domain
|
|
// model — branding, user, project, parties, deadline_rules, deadlines. A
|
|
// second docforge consumer implements its own resolvers against its own
|
|
// data and plugs them into a ResolverSet the same way.
|
|
|
|
import (
|
|
"time"
|
|
|
|
"mgit.msbls.de/m/paliad/internal/models"
|
|
"mgit.msbls.de/m/paliad/pkg/docforge"
|
|
)
|
|
|
|
// Compile-time conformance: each resolver satisfies docforge.VariableResolver.
|
|
var (
|
|
_ docforge.VariableResolver = firmResolver{}
|
|
_ docforge.VariableResolver = todayResolver{}
|
|
_ docforge.VariableResolver = userResolver{}
|
|
_ docforge.VariableResolver = proceduralEventResolver{}
|
|
_ docforge.VariableResolver = projectResolver{}
|
|
_ docforge.VariableResolver = partiesResolver{}
|
|
_ docforge.VariableResolver = deadlineResolver{}
|
|
)
|
|
|
|
// firmResolver populates firm.* from process-wide branding.
|
|
type firmResolver struct{}
|
|
|
|
func (firmResolver) Namespace() string { return "firm" }
|
|
func (firmResolver) Populate(bag PlaceholderMap) { addFirmVars(bag) }
|
|
|
|
// todayResolver populates today.* from the build-time clock.
|
|
type todayResolver struct{ now time.Time }
|
|
|
|
func (todayResolver) Namespace() string { return "today" }
|
|
func (r todayResolver) Populate(bag PlaceholderMap) { addTodayVars(bag, r.now) }
|
|
|
|
// userResolver populates user.* from the caller's row.
|
|
type userResolver struct{ user *models.User }
|
|
|
|
func (userResolver) Namespace() string { return "user" }
|
|
func (r userResolver) Populate(bag PlaceholderMap) { addUserVars(bag, r.user) }
|
|
|
|
// proceduralEventResolver populates procedural_event.* and the legacy
|
|
// rule.* alias from the published deadline_rule.
|
|
type proceduralEventResolver struct {
|
|
rule *models.DeadlineRule
|
|
lang string
|
|
}
|
|
|
|
func (proceduralEventResolver) Namespace() string { return "procedural_event" }
|
|
func (r proceduralEventResolver) Populate(bag PlaceholderMap) { addRuleVars(bag, r.rule, r.lang) }
|
|
|
|
// projectResolver populates project.* from the project + its proceeding type.
|
|
type projectResolver struct {
|
|
project *models.Project
|
|
pt *models.ProceedingType
|
|
lang string
|
|
}
|
|
|
|
func (projectResolver) Namespace() string { return "project" }
|
|
func (r projectResolver) Populate(bag PlaceholderMap) { addProjectVars(bag, r.project, r.pt, r.lang) }
|
|
|
|
// partiesResolver populates parties.* from the (already filtered) party list.
|
|
type partiesResolver struct{ parties []models.Party }
|
|
|
|
func (partiesResolver) Namespace() string { return "parties" }
|
|
func (r partiesResolver) Populate(bag PlaceholderMap) { addPartyVars(bag, r.parties) }
|
|
|
|
// deadlineResolver populates deadline.* from the next pending deadline.
|
|
type deadlineResolver struct {
|
|
deadline *models.Deadline
|
|
project *models.Project
|
|
lang string
|
|
}
|
|
|
|
func (deadlineResolver) Namespace() string { return "deadline" }
|
|
func (r deadlineResolver) Populate(bag PlaceholderMap) {
|
|
addDeadlineVars(bag, r.deadline, r.project, r.lang)
|
|
}
|