Documentation
¶
Overview ¶
Package validate runs the two-stage Prism spec validator:
- ShapeValidator runs the JSON Schema bundle from schema/embed.go, which catches structural errors (unknown fields, missing required, wrong types, oneOf misses).
- SemanticValidator (semantic.go) runs Go-side rules that need schema-aware reasoning (PRISM_SPEC_001..009).
The two stages are kept separate so a spec that fails shape never reaches the semantic stage; many semantic rules assume the spec is structurally well-formed.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RegisterDefault ¶
func RegisterDefault(f RuleFactory)
RegisterDefault appends a rule factory to the default rule set used by NewDefaultSemanticValidator. The rules package calls this in its init.
Types ¶
type CompositeLookup ¶
type CompositeLookup struct {
// contains filtered or unexported fields
}
CompositeLookup tries lookups in order and returns the first hit. Used by the CLI when a spec mixes inline datasets (StaticLookup) with real `.pulse` sources (PulseLookup) — both lookups share one SchemaLookup surface so semantic rules need no awareness.
func NewCompositeLookup ¶
func NewCompositeLookup(lookups ...SchemaLookup) *CompositeLookup
NewCompositeLookup constructs a CompositeLookup over the given lookups in priority order. nil lookups are skipped.
func (*CompositeLookup) Names ¶
func (c *CompositeLookup) Names() []string
Names implements the Namer interface (see Namer below) by unioning every constituent lookup's Names. Lookups that do not implement Names contribute nothing.
func (*CompositeLookup) Schema ¶
func (c *CompositeLookup) Schema(name string) (*PulseSchemaShim, bool)
Schema implements SchemaLookup.
type EmptyLookup ¶
type EmptyLookup struct{}
EmptyLookup is a SchemaLookup that finds nothing. Semantic rules that gate on schema presence (e.g. PRISM_SPEC_001) silently no-op when given an EmptyLookup, matching the P01 "no real Pulse source bound" mode.
func (EmptyLookup) Schema ¶
func (EmptyLookup) Schema(string) (*PulseSchemaShim, bool)
Schema implements SchemaLookup.
type FieldShim ¶
type FieldShim struct {
// Name is the field name as referenced by encodings / transforms.
Name string
// Type is the measure type bucket (nominal/ordinal/quantitative/temporal).
Type string
}
FieldShim is one field in a PulseSchemaShim.
type Namer ¶
type Namer interface {
Names() []string
}
Namer is the optional capability for SchemaLookup impls that can enumerate their registered dataset names. The dataset-reference rule (PRISM_SPEC_005) uses this to know which external datasets to treat as declared, on top of the in-spec `datasets:` block.
type PulseLookup ¶
type PulseLookup struct {
// contains filtered or unexported fields
}
PulseLookup is a SchemaLookup backed by a real Resolver. It is the production replacement for StaticLookup once a spec binds a dataset to a `.pulse` source (path, archive#shard, or cohort:<id>).
Construction:
pl := validate.NewPulseLookup(resolve.New(nil), afero.NewOsFs())
pl.Register("brand_scores", "testdata/cohorts/tiny.pulse")
Lookups resolve through the Resolver and fold the returned *encoding.Schema into the minimal *PulseSchemaShim that semantic rules consume. Results are cached by dataset name for the lifetime of the PulseLookup — Validate is a one-shot caller, so the cache is scoped to a single validation pass.
func NewPulseLookup ¶
func NewPulseLookup(r resolve.Resolver, fs afero.Fs) *PulseLookup
NewPulseLookup constructs a PulseLookup. resolver and fs must be non-nil; bindings start empty (use Register before validating).
func (*PulseLookup) Names ¶
func (l *PulseLookup) Names() []string
Names returns every registered dataset name in arbitrary order. Used by the dataset-ref semantic rule to enumerate externally declared datasets without forcing the rule to know about Resolver.
func (*PulseLookup) Register ¶
func (l *PulseLookup) Register(name, ref string)
Register binds a dataset name to a ref the Resolver knows how to open. Re-registration replaces the binding and invalidates the cache entry. A no-op when name or ref is empty.
func (*PulseLookup) Schema ¶
func (l *PulseLookup) Schema(name string) (*PulseSchemaShim, bool)
Schema implements SchemaLookup.
Resolves the dataset's ref via the Resolver, folds the Pulse schema into a *PulseSchemaShim, and returns it. Returns (nil, false) when the name is not registered or the Resolver returns an error — the validator interprets a missing schema as "no checks possible" rather than firing false-positive rule errors.
type PulseSchemaShim ¶
type PulseSchemaShim struct {
// Name is the dataset's logical name.
Name string
// Fields lists the field name → measure type ("nominal" |
// "ordinal" | "quantitative" | "temporal") in declaration order.
Fields []FieldShim
}
PulseSchemaShim is the minimal field-metadata shape used by P01 semantic rules. It carries just enough to satisfy rules 001 (field exists), 002 (agg/type compat), and 007 (scale/type compat).
TODO(P02): replace with real Pulse schema type once the resolver surfaces it. Keep the field shape stable so rule code does not change.
func (*PulseSchemaShim) Field ¶
func (s *PulseSchemaShim) Field(name string) (FieldShim, bool)
Field returns the FieldShim for name and whether it was found.
func (*PulseSchemaShim) FieldNames ¶
func (s *PulseSchemaShim) FieldNames() []string
FieldNames returns field names in declaration order.
type RuleFactory ¶
type RuleFactory func() SemanticRule
RuleFactory builds a SemanticRule; using a factory list keeps the rules sub-package free to import validate without an import cycle.
type SchemaLookup ¶
type SchemaLookup interface {
// Schema returns the schema for the named dataset and reports whether
// it was found.
Schema(dataset string) (*PulseSchemaShim, bool)
}
SchemaLookup resolves dataset name → minimal field metadata. Semantic rules that need to know whether a field exists or what type it is go through this interface so the validator stays decoupled from Pulse.
P01 ships StaticLookup (lookup_static.go) backed by a hand-built field table for tests. P02 will land a Pulse-backed implementation that walks real .pulse sources via the resolver.
type SemanticRule ¶
type SemanticRule interface {
// Code returns the canonical PRISM_SPEC_* identifier this rule emits.
Code() string
// Check runs the rule against the typed spec. It receives a
// SchemaLookup so rules that need dataset field metadata can resolve
// against the registered sources (real Pulse-backed implementation
// arrives in P02; P01 ships a static stub).
Check(s *spec.Spec, schemas SchemaLookup) []*errors.AppError
}
SemanticRule is one Prism spec rule that needs Go-side reasoning (cross-field, Pulse-schema-aware, expression-aware, etc.). Rules return zero or more AppError values; a non-empty slice means the rule fired.
type SemanticValidator ¶
type SemanticValidator struct {
// contains filtered or unexported fields
}
SemanticValidator runs an ordered set of SemanticRules against a spec. Construction is cheap; reuse across many specs.
func NewDefaultSemanticValidator ¶
func NewDefaultSemanticValidator() *SemanticValidator
NewDefaultSemanticValidator returns a SemanticValidator wired with the canonical Prism rule set (PRISM_SPEC_001 through PRISM_SPEC_009).
func NewSemanticValidator ¶
func NewSemanticValidator(rules ...SemanticRule) *SemanticValidator
NewSemanticValidator returns a SemanticValidator wired with the given rules in order. A nil/empty rules slice yields a validator that always returns zero errors.
func (*SemanticValidator) Rules ¶
func (v *SemanticValidator) Rules() []SemanticRule
Rules returns the rule list in order (defensive copy).
func (*SemanticValidator) Validate ¶
func (v *SemanticValidator) Validate(s *spec.Spec, schemas SchemaLookup) []*errors.AppError
Validate runs every rule against s using schemas. Errors from every rule are concatenated in rule order. Rules do not short-circuit each other; a failing rule does not stop subsequent rules.
type ShapeError ¶
type ShapeError struct {
// InstanceLocation is the JSON pointer to the offending instance node.
InstanceLocation string
// KeywordLocation is the JSON pointer to the schema keyword that failed.
KeywordLocation string
// Message is the human-readable failure description.
Message string
}
ShapeError is the validator-agnostic representation of one shape failure.
type ShapeValidator ¶
type ShapeValidator struct {
// contains filtered or unexported fields
}
ShapeValidator wraps a compiled JSON Schema for the Prism spec entry point. Construction is cheap once; callers should reuse a validator instance across many Validate calls.
func NewShapeValidator ¶
func NewShapeValidator() (*ShapeValidator, error)
NewShapeValidator builds a ShapeValidator backed by the embedded v1 schema bundle. Every schema file is registered under both its filename (so relative $refs like "data.schema.json#/$defs/data" resolve) and its URN $id (so urn:prism:schema:v1:spec self-refs in composition.json resolve).
func (*ShapeValidator) Validate ¶
func (v *ShapeValidator) Validate(spec any) []ShapeError
Validate runs the compiled JSON Schema against spec (which must be a generic JSON value: map[string]any / []any / scalars — i.e. the output of json.Unmarshal into interface{}). Returns a flat slice of shape errors; an empty slice means the spec is structurally valid.
type StaticLookup ¶
type StaticLookup struct {
Schemas map[string]*PulseSchemaShim
}
StaticLookup is a SchemaLookup backed by an in-memory map. Used for inline datasets (`data.values`, `data.fields`) and as a test fixture holder. The Pulse-backed sibling is PulseLookup (lookup_pulse.go); CompositeLookup mixes both when a spec uses both inline and source bindings.
func NewStaticLookup ¶
func NewStaticLookup() *StaticLookup
NewStaticLookup constructs an empty StaticLookup.
func (*StaticLookup) Names ¶
func (l *StaticLookup) Names() []string
Names implements the Namer interface (see lookup_pulse.go) by returning the registered dataset names in arbitrary order.
func (*StaticLookup) Register ¶
func (l *StaticLookup) Register(name string, schema *PulseSchemaShim)
Register adds or replaces the entry for the given dataset name.
func (*StaticLookup) Schema ¶
func (l *StaticLookup) Schema(name string) (*PulseSchemaShim, bool)
Schema implements SchemaLookup.