resolve

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 resolve maps user-facing dataset refs to a (ReadCloser, Schema) pair. Four ref forms are supported:

  • cohort.pulse local OsFs path
  • archive.pulse#shard.pulse anchor inside a Pulse shard archive
  • gs://bucket/path.pulse GCS-backed file (PUNT in v1: returns PRISM_RESOLVE_GCS_UNAVAILABLE)
  • cohort:<id> registry lookup; the resolved string is recursively resolved (one level)

Every read goes through an afero.Fs so tests can use NewMemMapFs. The Resolver does not cache; it leaves caching to the DAG executor via Table.Hash() + per-node fingerprint.

GCS is intentionally not wired in P02. Pulse 0.8.4 does not ship a generic GCS afero.Fs; adding a real GCS SDK in this phase violates the dep-parity rule and inflates v1 scope. See D027.

Index

Constants

View Source
const EnvDatasetVar = "PRISM_DATASETS"

EnvDatasetVar is the env var consulted by LoadDatasetRegistryEnv.

Variables

This section is empty.

Functions

This section is empty.

Types

type DataResolver

type DataResolver interface {
	ResolveData(ctx context.Context, ref string) (*Dataset, error)
}

DataResolver maps an opaque runtime reference to an inline dataset. Used to support the `data: {ref: "<name>"}` spec variant: the spec describes *what to draw*; the resolver provides *the data to draw it with*. Different environments resolve the same ref differently:

  • A browser environment looks up an injected dataset from the page runtime via prism.setDataResolver.
  • A server environment reads a pre-positioned file or queries a trusted backend.
  • A test environment returns canned rows from a fixture map.

Implementations should be safe for concurrent use — the executor may call Resolve from multiple goroutines when several layers reference the same ref.

func ChainDataResolvers

func ChainDataResolvers(resolvers ...DataResolver) DataResolver

ChainDataResolvers walks each resolver in order, returning the first non-unresolved result. Errors that are *not* ErrDataRefUnresolved short-circuit immediately. Nil resolvers in the chain are skipped silently.

type DataResolverFunc

type DataResolverFunc func(ctx context.Context, ref string) (*Dataset, error)

DataResolverFunc adapts a function to the DataResolver interface.

func (DataResolverFunc) ResolveData

func (f DataResolverFunc) ResolveData(ctx context.Context, ref string) (*Dataset, error)

ResolveData implements DataResolver.

type Dataset

type Dataset struct {
	Values []map[string]any
	Fields []spec.FieldSpec
}

Dataset is the in-memory dataset shape returned by a DataResolver. It carries row values + optional field schema, matching the inline `data: {values: [...]}` variant of the spec.

type DatasetLister

type DatasetLister interface {
	Names() []string
}

DatasetLister is an optional interface implementations may provide to enumerate the registered aliases. The Twirp `ListDatasets` RPC (P14) introspects registries by type-asserting to this interface; registries without a meaningful enumeration return nil and the RPC emits an empty list. Names returned are deduplicated across chained layers and sorted ascending.

type DatasetRegistry

type DatasetRegistry interface {
	// Resolve returns the backing ref for alias (path, anchor, gs://,
	// or cohort:<id>). The second return is false when the alias is
	// not registered.
	Resolve(alias string) (string, bool)
}

DatasetRegistry resolves spec-level dataset aliases (`{"data": {"name": "current"}}`) to backing refs the resolver understands (paths, archive#shard anchors, gs:// urls, cohort:<id>). The registry is distinct from the cohort-id Registry above: cohort-ids are an internal indirection layer; dataset aliases are user-facing names declared in server config or the spec's `datasets` block.

D008 documents the combined client + server registry strategy. P07's loader handles the server-side half (JSON file + env var); the browser-side `<prism-dataset>` mirror lands in P12.

func ChainDatasetRegistries

func ChainDatasetRegistries(layers ...DatasetRegistry) DatasetRegistry

ChainDatasetRegistries walks the supplied registries in order and returns the first hit per alias. Layers are tried left-to-right, so callers pass the highest-priority registry first. Nil entries are skipped.

func LoadDatasetRegistryEnv

func LoadDatasetRegistryEnv() DatasetRegistry

LoadDatasetRegistryEnv parses comma-separated `name=path` pairs from PRISM_DATASETS. Malformed entries (missing `=`, empty name, empty path) are silently dropped — callers can post-validate via `len(registry)` if they want to surface a config error.

func LoadDatasetRegistryFile

func LoadDatasetRegistryFile(path string, fs afero.Fs) (DatasetRegistry, error)

LoadDatasetRegistryFile parses a JSON file of shape

{"datasets": {"current": "cohorts/q1.pulse",
              "prior":   "cohorts/q4.pulse"}}

into a MapDatasetRegistry. YAML support is a documented TODO (D048): Pulse's go.mod does not ship a YAML loader and the dep-parity rule (Rule 13) forbids adding one in P07.

Returns an empty registry (not nil) when the file is absent so callers can chain it through ChainDatasetRegistries unconditionally.

type DefaultResolver

type DefaultResolver struct {
	// contains filtered or unexported fields
}

DefaultResolver is the production Resolver. It dispatches ref forms against the four known shapes and delegates to Pulse for the bulk of the work (single-file open, archive#shard anchor extraction).

Construction:

r := resolve.New(nil)            // EmptyRegistry, no cohort:<id> lookups
r := resolve.New(reg)            // custom Registry

The fs argument to Resolve is taken per-call so callers can swap between OsFs, MemMapFs, or BasePathFs without rebuilding the resolver.

func New

func New(reg Registry) *DefaultResolver

New constructs a DefaultResolver. Passing a nil registry yields an EmptyRegistry — every cohort:<id> ref will then return PRISM_RESOLVE_004.

func (*DefaultResolver) Resolve

func (r *DefaultResolver) Resolve(ref string, fs afero.Fs) (io.ReadCloser, *encoding.Schema, error)

Resolve implements Resolver.

type EmptyDatasetRegistry

type EmptyDatasetRegistry struct{}

EmptyDatasetRegistry rejects every Resolve. Useful when callers want to declare "no registry" without a nil check at every site.

func (EmptyDatasetRegistry) Names

func (EmptyDatasetRegistry) Names() []string

Names implements DatasetLister with the empty slice.

func (EmptyDatasetRegistry) Resolve

func (EmptyDatasetRegistry) Resolve(string) (string, bool)

Resolve implements DatasetRegistry.

type EmptyRegistry

type EmptyRegistry struct{}

EmptyRegistry rejects every lookup. Used when no registry is wired.

func (EmptyRegistry) Lookup

func (EmptyRegistry) Lookup(string) (string, bool)

Lookup implements Registry.

type ErrDataRefUnresolved

type ErrDataRefUnresolved struct {
	Ref string
}

ErrDataRefUnresolved signals that the caller's DataResolver could not satisfy the given ref. Plan / build surfaces it as PRISM_RESOLVE_REF_UNRESOLVED.

func (ErrDataRefUnresolved) Error

func (e ErrDataRefUnresolved) Error() string

Error implements error.

type MapDataResolver

type MapDataResolver map[string]*Dataset

MapDataResolver is a static map-backed resolver. Useful in tests and for embedding small canned datasets without writing a custom resolver. The zero value is a usable, empty resolver — Resolve returns ErrDataRefUnresolved for every ref.

func (MapDataResolver) ResolveData

func (m MapDataResolver) ResolveData(_ context.Context, ref string) (*Dataset, error)

ResolveData implements DataResolver.

type MapDatasetRegistry

type MapDatasetRegistry map[string]string

MapDatasetRegistry is the trivial in-memory implementation. The zero value is an empty registry; construction is `MapDatasetRegistry{...}` or `LoadDatasetRegistryFile`.

func (MapDatasetRegistry) Names

func (m MapDatasetRegistry) Names() []string

Names enumerates the alias keys for the in-memory registry. It returns a sorted, freshly-allocated slice (the caller may mutate the result without affecting the registry).

func (MapDatasetRegistry) Resolve

func (m MapDatasetRegistry) Resolve(alias string) (string, bool)

Resolve implements DatasetRegistry.

type MapRegistry

type MapRegistry map[string]string

MapRegistry is the default in-memory Registry. The zero value is an empty map; callers may also construct it via MapRegistry{"id": ref}.

func (MapRegistry) Lookup

func (m MapRegistry) Lookup(id string) (string, bool)

Lookup implements Registry.

type Registry

type Registry interface {
	Lookup(id string) (string, bool)
}

Registry maps a cohort id (`cohort:<id>`) to its backing ref (path, anchor, or gs:// URL). The default in-process implementation is MapRegistry; the dashboard/orchestrator wires its own at runtime.

type Resolver

type Resolver interface {
	Resolve(ref string, fs afero.Fs) (io.ReadCloser, *encoding.Schema, error)
}

Resolver resolves a ref to a streaming reader plus the cohort's schema. The returned ReadCloser, when non-nil, owns its underlying file handle; callers must Close it. Schema is always non-nil on success.

Concrete implementations:

  • DefaultResolver (resolve/default.go): the production path.
  • tests may swap in a fake that returns canned bytes + schema.

Jump to

Keyboard shortcuts

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