validate

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 27, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package validate runs the two-stage Prism spec validator:

  1. ShapeValidator runs the JSON Schema bundle from schema/embed.go, which catches structural errors (unknown fields, missing required, wrong types, oneOf misses).
  2. 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.

Directories

Path Synopsis
Package rules holds the individual SemanticRule implementations that fire PRISM_SPEC_* error codes.
Package rules holds the individual SemanticRule implementations that fire PRISM_SPEC_* error codes.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL