Documentation
¶
Overview ¶
Package figureout derives configuration decoding, validation, defaults and schema generation from a single typed declaration.
An application declares its configuration once with Derive, binding Go fields by pointer:
var ConfigDescriptor = figureout.MustDerive(
func(c *Config, s *figureout.Schema[Config]) {
figureout.Explicit(s, &c.Host, "host").NonEmpty()
figureout.Explicit(s, &c.Port, "port").InRange(1, 65535)
figureout.Value(s, &c.Banner, "banner")
figureout.Optional(s, &c.Timeout, "timeout").AtLeast(time.Second)
},
)
The registration function says what absence means: Explicit demands a value, Value resolves to the zero one, and Optional keeps the difference visible to the consumer.
The resulting Descriptor is immutable and format-neutral: sources project it into wire representations, schema targets emit it as JSON Schema or CUE.
Example ¶
Declare the configuration once, then resolve it from any source.
package main
import (
"fmt"
"github.com/go-faster/figureout"
"github.com/go-faster/figureout/source/yaml"
)
type quickstart struct {
Address string
Port int
Debug bool
}
func main() {
descriptor := figureout.MustDerive(func(c *quickstart, s *figureout.Schema[quickstart]) {
figureout.Explicit(s, &c.Address, "address").NonEmpty()
figureout.Value(s, &c.Port, "port").InRange(1, 65535).ApplyDefault(8080)
figureout.Value(s, &c.Debug, "debug").ApplyDefault(false)
})
cfg, _, err := descriptor.Resolve(
yaml.Bytes([]byte("address: 0.0.0.0\ndebug: true\n")),
)
if err != nil {
panic(err)
}
fmt.Printf("%s:%d debug=%v\n", cfg.Address, cfg.Port, cfg.Debug)
}
Output: 0.0.0.0:8080 debug=true
Example (Completeness) ¶
Every exported field must be registered or explicitly ignored, so a struct and its description cannot drift apart.
package main
import (
"fmt"
"github.com/go-faster/figureout"
)
type incomplete struct {
Address string
Port int
}
func main() {
_, err := figureout.Derive(func(c *incomplete, s *figureout.Schema[incomplete]) {
figureout.Value(s, &c.Address, "address")
})
fmt.Println(err)
}
Output: field.missing_definition [incomplete.Port]: incomplete.Port is neither registered nor explicitly ignored
Example (Enum) ¶
An enum takes its values from the type, so a stringer derivative stays the single source of truth.
descriptor := figureout.MustDerive(func(c *leveled, s *figureout.Schema[leveled]) {
figureout.Enum(s, &c.Level, "level").ApplyDefault(LogInfo)
})
cfg, _, err := descriptor.Resolve(env.Values(map[string]string{"LEVEL": "warn"}))
if err != nil {
panic(err)
}
fmt.Println("level:", cfg.Level)
_, _, err = descriptor.Resolve(env.Values(map[string]string{"LEVEL": "verbose"}))
fmt.Println("bad value:", err)
Output: level: warn bad value: constraint.type_mismatch [level]: must be one of [debug, info, warn, error], got verbose (env LEVEL)
Example (Erasing) ¶
An explicit null in a later layer erases what earlier layers set.
package main
import (
"fmt"
"time"
"github.com/go-faster/figureout"
"github.com/go-faster/figureout/source/yaml"
)
type erasable struct {
Level string
Timeout figureout.OptionalOf[time.Duration]
}
func main() {
descriptor := figureout.MustDerive(func(c *erasable, s *figureout.Schema[erasable]) {
figureout.Value(s, &c.Level, "level").ApplyDefault("info")
figureout.Optional(s, &c.Timeout, "timeout")
})
cfg, report, err := descriptor.Resolve(
yaml.Bytes([]byte("level: debug\ntimeout: 30s\n")),
yaml.Bytes([]byte("level: null\ntimeout: null\n")),
)
if err != nil {
panic(err)
}
erasedBy, _ := report.ErasedBy("level")
fmt.Printf("level=%s (erased by %s, so the default applies)\n", cfg.Level, erasedBy.Source)
fmt.Printf("timeout set=%v\n", cfg.Timeout.IsSet())
}
Output: level=info (erased by yaml, so the default applies) timeout set=false
Example (JsonSchema) ¶
The same descriptor generates a JSON Schema.
package main
import (
"fmt"
"github.com/go-faster/figureout"
"github.com/go-faster/figureout/schema/jsonschema"
)
type documented struct {
Address string
Port int
}
func main() {
descriptor := figureout.MustDerive(func(c *documented, s *figureout.Schema[documented]) {
figureout.Explicit(s, &c.Address, "address").Doc("Listen address.").NonEmpty()
figureout.Explicit(s, &c.Port, "port").InRange(1, 65535)
})
schema, _, err := jsonschema.Generate(descriptor, jsonschema.Semantic())
if err != nil {
panic(err)
}
fmt.Println(string(schema))
}
Output: { "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": false, "properties": { "$schema": { "type": "string" }, "address": { "description": "Listen address.", "minLength": 1, "type": "string" }, "port": { "maximum": 65535, "minimum": 1, "type": "integer" } }, "required": [ "address", "port" ], "type": "object" }
Example (Layering) ¶
Sources merge in order, and the report says where each value came from.
package main
import (
"fmt"
"github.com/go-faster/figureout"
"github.com/go-faster/figureout/source/env"
"github.com/go-faster/figureout/source/yaml"
)
type layered struct {
Address string
Port int
Tags []string
}
func main() {
descriptor := figureout.MustDerive(func(c *layered, s *figureout.Schema[layered]) {
figureout.Value(s, &c.Address, "address")
figureout.Value(s, &c.Port, "port")
figureout.Value(s, &c.Tags, "tags").MergeAppend().ApplyDefault([]string{})
})
cfg, report, err := descriptor.Resolve(
yaml.Bytes([]byte("address: 127.0.0.1\nport: 80\ntags: [base]\n")),
env.Values(map[string]string{"APP_PORT": "9090", "APP_TAGS": "extra"}, env.Prefix("APP_")),
)
if err != nil {
panic(err)
}
address, _ := report.OriginOf("address")
port, _ := report.OriginOf("port")
fmt.Printf("address=%s from %s\n", cfg.Address, address.Source)
fmt.Printf("port=%d from %s %s\n", cfg.Port, port.Source, port.Name)
fmt.Printf("tags=%v\n", cfg.Tags)
}
Output: address=127.0.0.1 from yaml port=9090 from env APP_PORT tags=[base extra]
Example (OneOf) ¶
A union selects between alternative shapes, tagged by a discriminator.
package main
import (
"fmt"
"github.com/go-faster/figureout"
"github.com/go-faster/figureout/source/yaml"
)
type storage struct {
Backend backend
}
type backend struct {
S3 *s3Backend
Local *localBackend
}
type s3Backend struct{ Bucket string }
type localBackend struct{ Path string }
func main() {
s3 := figureout.MustDerive(func(b *s3Backend, s *figureout.Schema[s3Backend]) {
figureout.Explicit(s, &b.Bucket, "bucket").NonEmpty()
})
local := figureout.MustDerive(func(b *localBackend, s *figureout.Schema[localBackend]) {
figureout.Explicit(s, &b.Path, "path").NonEmpty()
})
descriptor := figureout.MustDerive(func(c *storage, s *figureout.Schema[storage]) {
figureout.OneOf(s, &c.Backend, "backend",
figureout.Discriminator("type"),
figureout.Variant("s3", &c.Backend.S3, s3),
figureout.Variant("local", &c.Backend.Local, local),
)
})
cfg, _, err := descriptor.Resolve(yaml.Bytes([]byte("backend:\n type: s3\n bucket: configs\n")))
if err != nil {
panic(err)
}
fmt.Println("bucket:", cfg.Backend.S3.Bucket)
fmt.Println("local selected:", cfg.Backend.Local != nil)
_, _, err = descriptor.Resolve(yaml.Bytes([]byte("backend:\n type: gcs\n")))
fmt.Println("bad tag:", err)
}
Output: bucket: configs local selected: false bad tag: union.invalid [backend.type]: unknown variant "gcs", want one of [s3, local] (yaml backend.type)
Example (Optional) ¶
Optional distinguishes "no source provided it" from "provided as zero".
package main
import (
"fmt"
"time"
"github.com/go-faster/figureout"
"github.com/go-faster/figureout/source/json"
)
type optionalConfig struct {
Timeout figureout.OptionalOf[time.Duration]
}
func main() {
descriptor := figureout.MustDerive(func(c *optionalConfig, s *figureout.Schema[optionalConfig]) {
figureout.Optional(s, &c.Timeout, "timeout").AtLeast(time.Second)
})
for _, document := range []string{"{}", `{"timeout": "1m"}`} {
cfg, _, err := descriptor.Resolve(json.Bytes([]byte(document)))
if err != nil {
panic(err)
}
fmt.Printf("%-20s -> %v\n", document, cfg.Timeout)
}
}
Output: {} -> none {"timeout": "1m"} -> some(1m0s)
Index ¶
- Constants
- func CanonicalPath(path string) string
- func DiscriminatorPath(f *FieldModel) (string, bool)
- func ElementKey(path string) (collection, key string, ok bool)
- func ElementPath(path, key string) string
- func EnumValuesOf(f *FieldModel) ([]any, bool)
- func Ignore[R, C any](s *Schema[R], field *C, opts ...IgnoreOption)
- func IgnorePath[R any](s *Schema[R], path string, opts ...IgnoreOption)
- func IgnoreRecursive[R, C any](s *Schema[R], field *C, opts ...IgnoreOption)
- func IgnoreRecursivePath[R any](s *Schema[R], path string, opts ...IgnoreOption)
- func Invariant[T any](s *Schema[T], name string, check func(*T) error)
- func KeyedElementPath(path, field, value string) string
- func MustRegisterType[T any](r *TypeRegistry, opts ...TypeOption)
- func Redact(f *FieldModel, msg string, values ...any) string
- func RegisterType[T any](r *TypeRegistry, opts ...TypeOption) error
- type Assignment
- type CheckConstraint
- type Collection
- type CompletenessMode
- type Constraint
- type Decoder
- type Default
- type Descriptor
- type Diagnostic
- type Diagnostics
- type Element
- type Encoder
- type EnumConstraint
- type EnumSliceValuer
- type EnumValuer
- type FieldBuilder
- func (f *FieldBuilder) Deprecated(reason string) *FieldBuilder
- func (f *FieldBuilder) Doc(text string) *FieldBuilder
- func (f *FieldBuilder) Examples(values ...any) *FieldBuilder
- func (f *FieldBuilder) Hidden() *FieldBuilder
- func (f *FieldBuilder) Name() string
- func (f *FieldBuilder) Required() *FieldBuilder
- func (f *FieldBuilder) With(opts ...FieldOption) *FieldBuilder
- type FieldID
- type FieldModel
- func (f *FieldModel) Elements() (*ObjectModel, bool)
- func (f *FieldModel) MergeKey() (*FieldModel, bool)
- func (f *FieldModel) Moved() bool
- func (f *FieldModel) Opaque() (reason string, ok bool)
- func (f *FieldModel) OptionalSection() bool
- func (f *FieldModel) Recursive() (*ObjectModel, bool)
- func (f *FieldModel) Required() bool
- func (f *FieldModel) Shorthand() (Type, bool)
- func (f *FieldModel) Source(id SourceID) (*SourceProjection, bool)
- func (f *FieldModel) Validate(v any) error
- func (f *FieldModel) ZeroDefault() bool
- type FieldOption
- func AcceptShapes(id SourceID, shapes ...Shape) FieldOption
- func Check[T any](name string, fn func(T) error) FieldOption
- func Deprecated(reason string) FieldOption
- func Doc(text string) FieldOption
- func EnumOf[T EnumValuer[T]]() FieldOption
- func EnumOfFunc[T any](values func() []T) FieldOption
- func EnumOfSlice[T EnumSliceValuer[T]]() FieldOption
- func EnumOfValues[T any](values ...T) FieldOption
- func Examples(values ...any) FieldOption
- func Hidden() FieldOption
- func MovedFrom(paths ...string) FieldOption
- func Secret() FieldOption
- func Unit(u time.Duration) FieldOption
- func WithDecoder(id SourceID, d Decoder, shapes ...Shape) FieldOption
- type FieldOptionContext
- type FieldOptionFunc
- type FieldPath
- type IgnoreOption
- type InvariantModel
- type Layer
- type LengthConstraint
- type ListField
- type MapField
- type MergePolicy
- type Metadata
- type Model
- type ObjectField
- func Group[T any](s *Schema[T], name string, describe func(*Schema[T]), opts ...FieldOption) *ObjectField
- func Object[R, F, C any](s *Schema[R], field *F, name string, d *Descriptor[C], opts ...FieldOption) *ObjectField
- func ObjectFunc[R, F, C any](s *Schema[R], field *F, name string, describe func(*C, *Schema[C]), ...) *ObjectField
- func OptionalObject[R, F, C any](s *Schema[R], field *F, name string, d *Descriptor[C], opts ...FieldOption) *ObjectField
- func OptionalObjectFunc[R, F, C any](s *Schema[R], field *F, name string, describe func(*C, *Schema[C]), ...) *ObjectField
- func ScalarOr[R, C, S any](s *Schema[R], field *C, name string, d *Descriptor[C], widen func(S) C, ...) *ObjectField
- type ObjectModel
- type OpaqueField
- type OptionalOf
- func (o *OptionalOf[T]) Clear()
- func (o OptionalOf[T]) IsSet() bool
- func (o OptionalOf[T]) MarshalJSON() ([]byte, error)
- func (o OptionalOf[T]) MarshalYAML() (any, error)
- func (o OptionalOf[T]) OrElse(v T) T
- func (o *OptionalOf[T]) Set(v T)
- func (o OptionalOf[T]) String() string
- func (o *OptionalOf[T]) UnmarshalJSON(data []byte) error
- func (o *OptionalOf[T]) UnmarshalYAML(n *yaml.Node) error
- func (o OptionalOf[T]) Value() (T, bool)
- type Origin
- type PatternConstraint
- type PatternDialect
- type Presence
- type RangeConstraint
- type ReasonOption
- type Report
- type Schema
- type SchemaOption
- type Section
- type Severity
- type Shape
- type ShapeKind
- type Source
- type SourceID
- type SourceNamer
- type SourceProjection
- type TargetID
- type Type
- type TypeKind
- type TypeOption
- func BooleanType() TypeOption
- func Constrain(c Constraint) TypeOption
- func DurationType() TypeOption
- func InRange(minimum, maximum any) TypeOption
- func IntegerType() TypeOption
- func NumberType() TypeOption
- func StringType() TypeOption
- func TimestampType() TypeOption
- func TypeFieldOptions(opts ...FieldOption) TypeOption
- type TypeRegistry
- type Union
- type UnionField
- type UnionOption
- type ValueField
- func Enum[R any, T EnumValuer[T]](s *Schema[R], field *T, name string, opts ...FieldOption) *ValueField[T]
- func EnumFunc[R, T any](s *Schema[R], field *T, name string, values func() []T, opts ...FieldOption) *ValueField[T]
- func EnumSlice[R any, T EnumSliceValuer[T]](s *Schema[R], field *T, name string, opts ...FieldOption) *ValueField[T]
- func EnumValues[R, T any](s *Schema[R], field *T, name string, values []T, opts ...FieldOption) *ValueField[T]
- func Explicit[R, T any](s *Schema[R], field *T, name string, opts ...FieldOption) *ValueField[T]
- func Optional[R, T any](s *Schema[R], field *OptionalOf[T], name string, opts ...FieldOption) *ValueField[T]
- func Value[R, T any](s *Schema[R], field *T, name string, opts ...FieldOption) *ValueField[T]
- func (f *ValueField[T]) ApplyDefault(v T) *ValueField[T]
- func (f *ValueField[T]) AtLeast(minimum T) *ValueField[T]
- func (f *ValueField[T]) AtMost(maximum T) *ValueField[T]
- func (f *ValueField[T]) Check(name string, fn func(T) error) *ValueField[T]
- func (f *ValueField[T]) Deprecated(reason string) *ValueField[T]
- func (f *ValueField[T]) Doc(text string) *ValueField[T]
- func (f *ValueField[T]) DocumentDefault(v T) *ValueField[T]
- func (f *ValueField[T]) Enum(values ...T) *ValueField[T]
- func (f *ValueField[T]) Examples(values ...T) *ValueField[T]
- func (f *ValueField[T]) GreaterThan(minimum T) *ValueField[T]
- func (f *ValueField[T]) Hidden() *ValueField[T]
- func (f *ValueField[T]) InRange(minimum, maximum T) *ValueField[T]
- func (f *ValueField[T]) LessThan(maximum T) *ValueField[T]
- func (f *ValueField[T]) MaxItems(n uint64) *ValueField[T]
- func (f *ValueField[T]) MaxLength(n uint64) *ValueField[T]
- func (f *ValueField[T]) MergeAppend() *ValueField[T]
- func (f *ValueField[T]) MergeByKey() *ValueField[T]
- func (f *ValueField[T]) MergeReplace() *ValueField[T]
- func (f *ValueField[T]) MinItems(n uint64) *ValueField[T]
- func (f *ValueField[T]) MinLength(n uint64) *ValueField[T]
- func (f *ValueField[T]) NonEmpty() *ValueField[T]
- func (f *ValueField[T]) Pattern(expr string) *ValueField[T]
- func (f *ValueField[T]) Required() *ValueField[T]
- func (f *ValueField[T]) With(opts ...FieldOption) *ValueField[T]
- type ValueState
- type VariantModel
- type Violation
Examples ¶
Constants ¶
const ( CodeMissingDefinition = "field.missing_definition" CodeDuplicateField = "field.duplicate_registration" CodeForeignPointer = "field.foreign_pointer" CodeAmbiguousZeroSize = "field.ambiguous_zero_size" CodeUnsupportedType = "field.unsupported_type" CodeDuplicateName = "name.duplicate" CodeSourceNameCollision = "source.name_collision" CodeSourceUnsupported = "source.unsupported" CodeConstraintMismatch = "constraint.type_mismatch" CodeDefaultMismatch = "default.type_mismatch" CodeUnionInvalid = "union.invalid" CodeDeprecated = "field.deprecated" CodeMovedConflict = "field.moved_conflict" CodeInvariantViolated = "invariant.violated" CodeValidatorNotExport = "validator.not_exportable" )
Diagnostic codes reported by descriptor compilation and schema generation.
const Redacted = "[redacted]"
Redacted replaces a secret value wherever the library would otherwise format one.
Variables ¶
This section is empty.
Functions ¶
func CanonicalPath ¶ added in v0.3.0
CanonicalPath rewrites every element subscript to the empty one, turning a concrete path into the model path describing it.
"sites[name=docs].max_bytes" becomes "sites[].max_bytes", which is what Model.FieldByPath indexes.
func DiscriminatorPath ¶
func DiscriminatorPath(f *FieldModel) (string, bool)
DiscriminatorPath returns the canonical path of a union field's discriminator property.
func ElementKey ¶ added in v0.3.0
ElementKey returns the subscript of an element path, and the collection it belongs to.
func ElementPath ¶ added in v0.3.0
ElementPath returns the path of one element of the collection at path.
func EnumValuesOf ¶
func EnumValuesOf(f *FieldModel) ([]any, bool)
EnumValuesOf returns the allowed values declared for a field, if any.
func Ignore ¶
func Ignore[R, C any](s *Schema[R], field *C, opts ...IgnoreOption)
Ignore marks a field as deliberately not part of the configuration.
Only the field itself is ignored; nested fields still have to be accounted for. Use IgnoreRecursive to ignore a whole subtree.
func IgnorePath ¶
func IgnorePath[R any](s *Schema[R], path string, opts ...IgnoreOption)
IgnorePath ignores a field by Go path, such as "Server.Marker".
Distinct zero-sized fields share an address, so pointer identity cannot select between them; path-based ignores are the supported way to handle them.
func IgnoreRecursive ¶
func IgnoreRecursive[R, C any](s *Schema[R], field *C, opts ...IgnoreOption)
IgnoreRecursive marks a field and every field below it as deliberately not part of the configuration.
func IgnoreRecursivePath ¶
func IgnoreRecursivePath[R any](s *Schema[R], path string, opts ...IgnoreOption)
IgnoreRecursivePath ignores a field and its subtree by Go path.
func Invariant ¶ added in v0.2.0
Invariant registers a rule that spans fields, checked once the configuration has resolved.
Constraints are per field, and real configurations are full of rules that are not: a key that must name an entry in another map, a flag that only takes effect when a credential is set, a lease that must outlast a timeout. Without somewhere to put them they become a hand-written pass after Resolve, which loses both the schema and the provenance the descriptor already has.
figureout.Invariant(s, "proxy-exists", func(c *Config) error {
for i, site := range c.Fetch.Sites {
if _, ok := c.Proxies[site.Proxy]; !ok {
return figureout.At(fmt.Sprintf("fetch.sites[%d].proxy", i)).
Errorf("no proxy named %q is configured", site.Proxy)
}
}
return nil
})
Invariants run only after every field resolved and validated, so a failure is never a consequence of an error already reported. Returning At keeps the origin of the offending value; a plain error is reported without one. Return several with errors.Join.
Registered on a nested ObjectFunc schema, an invariant sees the nested struct and its violation paths are prefixed with the nested object's path.
func KeyedElementPath ¶ added in v0.3.0
KeyedElementPath returns the path of a list element identified by a key field, as "sites[name=docs]".
func MustRegisterType ¶
func MustRegisterType[T any](r *TypeRegistry, opts ...TypeOption)
MustRegisterType is like RegisterType but panics on error.
func Redact ¶ added in v0.2.0
func Redact(f *FieldModel, msg string, values ...any) string
Redact removes every rendering of values from msg when f is a secret field.
Sources call it before reporting a decoding failure, so that "invalid integer \"hunter2\"" never reaches a log. Over-redaction is the safe direction: a message may lose more than the value itself, and that is preferred to leaking it.
func RegisterType ¶
func RegisterType[T any](r *TypeRegistry, opts ...TypeOption) error
RegisterType describes the named type T.
Types ¶
type Assignment ¶
type Assignment struct {
// Path is the canonical dotted path of the field.
Path string
State ValueState
Value any
Origin Origin
}
Assignment is one value produced by a source.
type CheckConstraint ¶
CheckConstraint is an opaque runtime validator.
It never contributes to generated schemas; emitters report CodeValidatorNotExport instead of silently implying coverage.
func (CheckConstraint) Applies ¶
func (CheckConstraint) Applies(TypeKind) bool
Applies implements Constraint.
func (CheckConstraint) Validate ¶
func (c CheckConstraint) Validate(v any) error
Validate implements Constraint.
type Collection ¶ added in v0.3.0
type Collection struct{}
Collection is the value a source assigns to a list or map of objects itself, to say that this layer provided the collection.
Its elements arrive as assignments of their own, so the merge policy needs some way to tell "this layer replaced the list" from "this layer said nothing about it" — an empty list has no elements to speak for it either way.
type CompletenessMode ¶
type CompletenessMode uint8
CompletenessMode selects which Go fields must be accounted for.
const ( // CompletenessExported requires every exported field to be registered, // delegated, covered by registered descendants, or ignored. CompletenessExported CompletenessMode = iota // CompletenessStrict additionally requires unexported fields to be // explicitly ignored. CompletenessStrict // CompletenessTagged considers only fields carrying the configured tag. CompletenessTagged // CompletenessDisabled performs no completeness validation. CompletenessDisabled )
Completeness modes.
type Constraint ¶
type Constraint interface {
// Kind identifies the constraint for emitters and diagnostics.
Kind() string
// Validate reports whether v satisfies the constraint.
Validate(v any) error
// Applies reports whether the constraint is meaningful for kind.
Applies(kind TypeKind) bool
}
Constraint is a declarative rule over a semantic value.
Constraints are values rather than closures so that validators, schema emitters and documentation can all consume the same declaration. Rules that cannot be expressed declaratively use CheckConstraint, which is runtime-only.
type Decoder ¶
Decoder converts a raw source value into a semantic value.
A decoder is opaque, so it must be accompanied by the shapes it accepts; otherwise schema generation cannot describe the field.
type Default ¶
Default is a field default.
Applied defaults change the resolved value; documented defaults only contribute metadata. The distinction matters for OptionalOf, where applying a default turns a missing value into a present one.
type Descriptor ¶
type Descriptor[T any] struct { // contains filtered or unexported fields }
Descriptor is an immutable compiled configuration description.
It is safe for concurrent use. Build one with Derive or MustDerive.
func Derive ¶
func Derive[T any](describe func(*T, *Schema[T]), opts ...SchemaOption) (*Descriptor[T], error)
Derive compiles a descriptor for T.
It allocates a synthetic zero value of T, passes its address to describe, resolves every registered pointer to a Go field, validates completeness and consistency, and compiles an immutable descriptor. The runtime values of the synthetic object are never used as defaults.
func MustDerive ¶
func MustDerive[T any](describe func(*T, *Schema[T]), opts ...SchemaOption) *Descriptor[T]
MustDerive is like Derive but panics on error.
The panic message contains every compilation diagnostic, not only the first.
A failed derivation is a programming error, so a package-level "var ConfigDescriptor = figureout.MustDerive(...)" is the intended idiom for a library. A binary that would rather report the failure than crash in init should derive inside its own loader instead:
var descriptor = sync.OnceValues(func() (*figureout.Descriptor[Config], error) {
return figureout.Derive(describe)
})
func (*Descriptor[T]) Model ¶
func (d *Descriptor[T]) Model() *Model
Model returns the compiled model. The returned value must not be mutated.
func (*Descriptor[T]) Resolve ¶
func (d *Descriptor[T]) Resolve(sources ...Source) (T, *Report, error)
Resolve decodes, merges, validates and materializes a configuration.
Later sources override earlier ones. The returned report carries provenance and diagnostics even when an error is returned.
func (*Descriptor[T]) ResolveContext ¶
ResolveContext is Descriptor.Resolve with a context.
func (*Descriptor[T]) Value ¶
func (d *Descriptor[T]) Value(cfg *T, path string) (any, bool)
Value reads the value at a canonical path out of a resolved configuration.
It reports false when the path is unknown, when the value is carried by an unset OptionalOf, or when the path is inside a variant that was not selected.
type Diagnostic ¶
type Diagnostic struct {
Severity Severity
Code string
Message string
// FieldPath is the canonical configuration path, such as "server.port".
FieldPath string
// GoPath is the Go path, such as "Config.Server.Port".
GoPath string
// MovedTo is the path superseding [Diagnostic.FieldPath], carried by
// [CodeDeprecated] and [CodeMovedConflict] when the deprecation is a move.
//
// It is what the message names in prose, as data: an application that
// phrases its own warnings reads it instead of parsing [Diagnostic.Message].
// It is empty for a field deprecated without a replacement, and for every
// other code.
MovedTo string
Source SourceID
Target TargetID
Origin *Origin
}
Diagnostic is a structured problem report.
type Diagnostics ¶
type Diagnostics []Diagnostic
Diagnostics is a collection of Diagnostic.
func (Diagnostics) Err ¶
func (ds Diagnostics) Err() error
Err returns ds if it contains errors, and nil otherwise.
func (Diagnostics) Error ¶
func (ds Diagnostics) Error() string
Error implements [error]. It formats every diagnostic, not just the first.
func (Diagnostics) HasErrors ¶
func (ds Diagnostics) HasErrors() bool
HasErrors reports whether any diagnostic has SeverityError.
type Element ¶ added in v0.7.0
type Element struct{}
Element is the value a source assigns to one element of a collection, to say that this layer contained it.
An element whose every member is absent has no assignment of its own to allocate its slot, and an element that resolves entirely to defaults is still an element: without a marker of its own it would vanish from the list rather than materialize with its defaults.
type EnumConstraint ¶
type EnumConstraint struct {
Values []any
}
EnumConstraint restricts a value to a set of allowed values.
It is deliberately distinct from a union: an enum narrows one scalar type, while a Union selects between alternative shapes.
func (EnumConstraint) Applies ¶
func (EnumConstraint) Applies(kind TypeKind) bool
Applies implements Constraint.
func (EnumConstraint) Validate ¶
func (c EnumConstraint) Validate(v any) error
Validate implements Constraint.
type EnumSliceValuer ¶
type EnumSliceValuer[T any] interface { Values() []T }
EnumSliceValuer is the slice-returning form of EnumValuer, as emitted by stringer derivatives that attach a method to the type.
type EnumValuer ¶
EnumValuer is a type that enumerates its own values.
It is the primary enum contract: because it is a constraint rather than a reflective probe, a type without values is a compile error rather than a descriptor diagnostic.
func (LogLevel) AllValues() iter.Seq[LogLevel] {
return slices.Values(logLevels)
}
type FieldBuilder ¶
type FieldBuilder struct {
// contains filtered or unexported fields
}
FieldBuilder is the fluent surface shared by every registered field.
Methods are no-ops when the registration already failed, so a describe callback never panics on a bad pointer; the failure is reported as a compilation diagnostic instead.
func (*FieldBuilder) Deprecated ¶
func (f *FieldBuilder) Deprecated(reason string) *FieldBuilder
Deprecated marks the field as deprecated.
func (*FieldBuilder) Doc ¶
func (f *FieldBuilder) Doc(text string) *FieldBuilder
Doc attaches documentation.
func (*FieldBuilder) Examples ¶
func (f *FieldBuilder) Examples(values ...any) *FieldBuilder
Examples attaches example values.
func (*FieldBuilder) Hidden ¶
func (f *FieldBuilder) Hidden() *FieldBuilder
Hidden hides the field from generated documentation.
func (*FieldBuilder) Name ¶
func (f *FieldBuilder) Name() string
Name returns the canonical name of the field.
func (*FieldBuilder) Required ¶ added in v0.4.0
func (f *FieldBuilder) Required() *FieldBuilder
Required makes absence an error instead of a value.
It is meaningful for a collection, whose absence otherwise resolves to empty, and for a Value field, whose absence otherwise resolves to the zero value; "Value(...).Required()" is Explicit spelled the long way. An OptionalOf carrier says the opposite by construction.
func (*FieldBuilder) With ¶
func (f *FieldBuilder) With(opts ...FieldOption) *FieldBuilder
With applies field options after registration.
type FieldModel ¶
type FieldModel struct {
ID FieldID
// Name is the canonical name within the declaring object.
Name string
// Path is the canonical dotted path from the descriptor root.
Path string
// GoPath locates the Go field relative to the declaring object.
GoPath FieldPath
// GoName is the Go path for diagnostics, such as "Config.Server.Port".
GoName string
Type Type
Presence Presence
Meta Metadata
Default *Default
Constraints []Constraint
// Merge decides how the field combines values from several layers.
Merge MergePolicy
Sources map[SourceID]*SourceProjection
Targets map[TargetID][]any
// MovedFrom lists the former paths of the field, relative to the
// descriptor that declares it. Each appears in the model as a deprecated
// shadow field carrying [FieldModel.MovedTo].
MovedFrom []string
// MovedTo is the canonical path superseding this field, set on the shadow
// fields [MovedFrom] creates. It is empty for a field of its own.
MovedTo string
// contains filtered or unexported fields
}
FieldModel is a compiled configuration field.
func (*FieldModel) Elements ¶ added in v0.3.0
func (f *FieldModel) Elements() (*ObjectModel, bool)
Elements reports whether the field is a collection of described objects.
Sources use it to decide whether to bind elements to their own paths; one that cannot express a collection of objects, such as environment variables, skips the field instead of inventing an index convention.
func (*FieldModel) MergeKey ¶ added in v0.3.0
func (f *FieldModel) MergeKey() (*FieldModel, bool)
MergeKey returns the element field identifying a list element across layers, as set by ListField.MergeByKey.
func (*FieldModel) Moved ¶ added in v0.2.0
func (f *FieldModel) Moved() bool
Moved reports whether the field is a deprecated former spelling of another one. A moved field is never materialized: its value is redirected to FieldModel.MovedTo during resolution.
func (*FieldModel) Opaque ¶ added in v0.7.0
func (f *FieldModel) Opaque() (reason string, ok bool)
Opaque reports whether the field carries a subtree verbatim, and why.
Targets use it to describe a passthrough rather than to describe what is inside it, which nothing knows.
func (*FieldModel) OptionalSection ¶ added in v0.7.0
func (f *FieldModel) OptionalSection() bool
carried is the Go type the carrier holds, which is a pointer to elem when the carrier is indirect. OptionalSection reports a nested object a source may leave out, whichever of the optional carriers spells it — an OptionalOf, with or without a pointer inside it, or a bare pointer.
A Group is never one. It nests the document without nesting the Go struct, so it has no field to hold "no section" in, and its members belong to whatever encloses it.
func (*FieldModel) Recursive ¶ added in v0.7.0
func (f *FieldModel) Recursive() (*ObjectModel, bool)
Recursive returns the configuration object the field re-enters, and reports whether it re-enters one at all.
A configuration type may refer to itself: a node with a child node, a rule with nested rules. Every value is still a finite tree — recursion reaches a descriptor only through a pointer, a slice or a map, each of which may simply be absent — so the cycle exists in the type graph alone, and the model is a graph rather than an infinitely deep tree. The field closing the cycle points back at an object that already encloses it instead of at a copy of it.
A target that names a shape emits the object once and refers to it. A target that spells one flat name per path, such as environment variables, has no bounded name for an unbounded path and stops here, the way it already stops at a collection of objects.
func (*FieldModel) Required ¶ added in v0.4.0
func (f *FieldModel) Required() bool
Required reports whether a source has to provide the field.
A field registered with Explicit is required unless it carries an applied default, a collection included. A field registered with Value is not: its absence resolves to the zero value, and a collection to an empty one, because an absent list and an empty one are the same statement about the world. Both opt back in with FieldBuilder.Required.
func (*FieldModel) Shorthand ¶ added in v0.2.0
func (f *FieldModel) Shorthand() (Type, bool)
Shorthand reports whether the field accepts a scalar in place of its object, and returns the scalar type it accepts.
Sources without an object syntax use it to bind the scalar spelling at the object's own name.
func (*FieldModel) Source ¶
func (f *FieldModel) Source(id SourceID) (*SourceProjection, bool)
Source returns the projection of the field for the given source.
func (*FieldModel) Validate ¶
func (f *FieldModel) Validate(v any) error
Validate runs every declarative and opaque constraint against v.
func (*FieldModel) ZeroDefault ¶ added in v0.5.0
func (f *FieldModel) ZeroDefault() bool
ZeroDefault reports whether absence resolves to the zero value rather than to a diagnostic. It is what Value declares and Explicit withholds.
A field with an applied Default never reports true: the default is what absence resolves to, and it is visible as one.
type FieldOption ¶
type FieldOption interface {
ApplyFieldOption(FieldOptionContext) error
}
FieldOption customizes a field registration.
Options are deliberately not generic over the field's value type: Go cannot infer a type argument for a nested call such as env.Name("PORT"), so a generic FieldOption[V] would force every option call site to spell the type. Value-typed operations live on the returned fluent builder instead.
func AcceptShapes ¶
func AcceptShapes(id SourceID, shapes ...Shape) FieldOption
AcceptShapes declares the wire shapes a source accepts for a field.
Source packages normally wrap this in their own option, such as json.Accepts. Declaring shapes is what lets schema generation describe a custom decoder that would otherwise be opaque.
func Check ¶
func Check[T any](name string, fn func(T) error) FieldOption
Check builds an opaque runtime validator option with a typed callback.
The type argument is inferred from fn, so a call site reads as figureout.Check("even", func(v int) error { ... }).
func Deprecated ¶
func Deprecated(reason string) FieldOption
Deprecated marks a field as deprecated with a reason.
Setting a deprecated field is reported as a SeverityWarning diagnostic in the Report. To also accept a former spelling, use MovedFrom.
func EnumOf ¶
func EnumOf[T EnumValuer[T]]() FieldOption
EnumOf builds an enum constraint option for a type that enumerates its own values. Use it with [Field] for carriers the enum helpers do not spell, such as OptionalOf[LogLevel].
func EnumOfFunc ¶
func EnumOfFunc[T any](values func() []T) FieldOption
EnumOfFunc builds an enum constraint option from a values function.
func EnumOfSlice ¶
func EnumOfSlice[T EnumSliceValuer[T]]() FieldOption
EnumOfSlice is EnumOf for types implementing EnumSliceValuer.
func EnumOfValues ¶
func EnumOfValues[T any](values ...T) FieldOption
EnumOfValues builds an enum constraint option from explicit values.
func Examples ¶
func Examples(values ...any) FieldOption
Examples attaches example values to a field.
func Hidden ¶
func Hidden() FieldOption
Hidden hides a field from generated documentation and help text.
func MovedFrom ¶ added in v0.2.0
func MovedFrom(paths ...string) FieldOption
MovedFrom accepts a former path of the field and reports its use.
Deprecated is metadata: it says a key is going away without doing anything when the key is set. MovedFrom is the behavior a configuration actually needs while it is being reshaped:
figureout.Value(s, &c.HTTPAddr, "http_addr", figureout.MovedFrom("addr"))
- the old spelling still resolves, with a [SeverityWarning] diagnostic in
the [Report] naming both paths
- setting both spellings is a [SeverityError], not a precedence rule: two
spellings in one configuration are two intentions, and silently picking
one is the worst available answer
- the old path appears in generated schemas as a deprecated property
The path is relative to the descriptor that declares the field, so it may name a former level: MovedFrom("legacy.addr") reads the old nesting. Levels that no longer exist are synthesized as deprecated objects; a level that is a nested descriptor of its own is reported rather than modified.
That scope decides how to reshape a flat legacy key into a section, which is the main thing MovedFrom exists for. Use Group, which keeps the field declared by the root schema, so a root-relative former path is in scope:
figureout.Group(s, "api", func(s *figureout.Schema[Config]) {
figureout.Value(s, &c.API.HTTPAddr, "http_addr",
figureout.MovedFrom("http_addr"))
})
The same registration inside ObjectFunc cannot express it: the field is declared by the nested descriptor, where "http_addr" resolves to the field itself rather than to the document root, and is reported as such.
A former path is a fact about documents, not about environment variables: sources that derive a name from the path skip a former one, because "database_dsn" and "database.dsn" derive the same variable and binding both would collide by construction. Where a variable really did exist under an old name, name it with that source's alias option.
func Secret ¶ added in v0.2.0
func Secret() FieldOption
Secret marks a field as carrying a credential.
Hidden is documentation metadata: it keeps a field out of generated docs and does nothing else, so a Pattern or MinLength failure is one constraint away from printing a token into a log. Secret has teeth. A secret field's value never appears in a message the library formats — not in a constraint failure, not in a decoding error from a source — and Report.Secret lets a consumer honor the same rule in its own logging.
figureout.Explicit(s, &c.Token, "token", figureout.Secret()).NonEmpty()
Secret redacts values, not names. A credential still appears in generated documentation, because its name is what an operator needs in order to supply it, while its default and examples render as Redacted. Pair it with Hidden to leave a field out of the reference entirely. Generated JSON Schema marks the property "writeOnly".
func Unit ¶ added in v0.2.0
func Unit(u time.Duration) FieldOption
Unit lets a duration field be written as a bare number of u.
Unit-suffixed integer keys outlive the configurations that introduced them, and moving one onto time.Duration normally means changing what the key accepts — 180 would have to become "180s", which breaks every deployment already running. A unit keeps the key and still resolves a time.Duration:
figureout.Value(s, &c.Timeout, "timeout_seconds", figureout.Unit(time.Second)) timeout_seconds: 180 // 180 * time.Second timeout_seconds: "3m" // still accepted, so a rename is a pure alias change
Generated schemas describe the canonical form: an integer, with the unit named in the description.
func WithDecoder ¶
func WithDecoder(id SourceID, d Decoder, shapes ...Shape) FieldOption
WithDecoder installs a source decoder together with the shapes it accepts.
A decoder is opaque, so the shapes are mandatory: without them, schema generation cannot describe what the source will accept, and nothing decides what reaches the decoder. A shape the field did not declare is rejected before the decoder runs.
The decoder owns every shape it declared, including object and array ones the semantic type cannot describe — which is what makes it the way to keep parsing a carrier that is going away:
figureout.Value(s, &c.Token, "token",
figureout.WithDecoder(yaml.Source, carrierDecoder{},
figureout.Shape{Kind: figureout.ShapeString},
figureout.Shape{Kind: figureout.ShapeObject, Fields: map[string]figureout.Shape{
"env": {Kind: figureout.ShapeString},
})))
A tree source hands over the node as []any, map[string]any or the scalar it decoded; sources whose values are text, such as env and file, hand over the text. Null never reaches a decoder: it stays a merge directive that erases.
type FieldOptionContext ¶
type FieldOptionContext interface {
// Name returns the canonical field name.
Name() string
// GoName returns the Go path of the field, for diagnostics.
GoName() string
// Type returns the semantic type of the field.
Type() Type
// Presence returns how the field models absence.
Presence() Presence
AddConstraint(Constraint) error
AddMetadata(Metadata) error
AddTargetAnnotation(TargetID, any) error
// SetUnit scales bare numbers written for a duration field.
SetUnit(time.Duration) error
// AddMovedFrom records a former path of the field.
AddMovedFrom(string) error
// SetReason documents why a field is not described. It applies to an
// opaque passthrough, which is the only field that is not.
SetReason(string) error
// SetSourceNames sets the primary name and aliases for a source.
SetSourceNames(SourceID, ...string) error
// AddSourceShapes declares the wire shapes a source accepts.
AddSourceShapes(SourceID, ...Shape) error
// SetSourceDecoder installs a decoder. A decoder without declared shapes
// is reported by schema generation.
SetSourceDecoder(SourceID, Decoder) error
// SkipSource excludes the field from a source.
SkipSource(SourceID) error
// AddSourceOption attaches source-specific settings.
AddSourceOption(SourceID, any) error
}
FieldOptionContext is the controlled surface an option may mutate.
It exposes registration methods rather than internal state, so adapter packages can extend the core without importing its internals.
type FieldOptionFunc ¶
type FieldOptionFunc func(FieldOptionContext) error
FieldOptionFunc adapts a function to FieldOption.
func (FieldOptionFunc) ApplyFieldOption ¶
func (f FieldOptionFunc) ApplyFieldOption(c FieldOptionContext) error
ApplyFieldOption implements FieldOption.
type FieldPath ¶
FieldPath locates a Go field relative to the object declaring it.
Index is the canonical identity: it survives padding changes and is usable with reflect.Value.FieldByIndex. Offset is kept for pointer validation and fast access, and is never the sole identity.
type IgnoreOption ¶
type IgnoreOption interface {
// contains filtered or unexported methods
}
IgnoreOption customizes an ignore declaration.
type InvariantModel ¶ added in v0.2.0
type InvariantModel struct {
Name string
}
InvariantModel names a cross-field rule in the compiled model.
Only the name is format-neutral: the rule itself is a Go function, so no target can emit it. Documentation generators list it so that a reader knows a rule exists which the schema does not describe.
type Layer ¶
type Layer struct {
Source SourceID
Assignments []Assignment
Diagnostics Diagnostics
}
Layer is the partial configuration decoded from one source.
Sources never write into the destination struct: they produce layers, which are merged in precedence order before materialization.
type LengthConstraint ¶
LengthConstraint bounds the length of a string, list, map or byte slice.
func (LengthConstraint) Applies ¶
func (LengthConstraint) Applies(kind TypeKind) bool
Applies implements Constraint.
func (LengthConstraint) Validate ¶
func (c LengthConstraint) Validate(v any) error
Validate implements Constraint.
type ListField ¶ added in v0.3.0
type ListField struct{ *FieldBuilder }
ListField is the fluent builder for a list of objects.
func List ¶ added in v0.3.0
func List[R, E any]( s *Schema[R], field *[]E, name string, d *Descriptor[E], opts ...FieldOption, ) *ListField
List registers a list whose elements are described by their own descriptor.
It is ListOf for an element description shared by several parents, or one an adopter wants to export.
func ListOf ¶ added in v0.3.0
func ListOf[R, E any]( s *Schema[R], field *[]E, name string, describe func(*E, *Schema[E]), opts ...FieldOption, ) *ListField
ListOf registers a list whose elements are configuration objects, described inline.
A list of objects is the part of a configuration file an operator actually edits, and describing it is what lets its elements have names, defaults, constraints, provenance and a schema:
figureout.ListOf(s, &c.Sites, "sites", func(e *Site, s *figureout.Schema[Site]) {
figureout.Explicit(s, &e.Name, "name").NonEmpty()
figureout.Value(s, &e.MaxBytes, "max_bytes").ApplyDefault(0)
})
Each element binds to its own path — "sites[0].max_bytes" — so merging, erasure and Report.OriginOf all work on an element the way they work on any other field. See ListField.MergeByKey to identify elements by one of their own fields instead of by position.
func (*ListField) MergeAppend ¶ added in v0.3.0
MergeAppend concatenates the elements of every layer, in layer order.
func (*ListField) MergeByKey ¶ added in v0.3.0
MergeByKey identifies elements by one of their own fields, so a later layer edits an element rather than restating the list.
figureout.ListOf(s, &c.Sites, "sites", describeSite).MergeByKey("name")
# base.yaml # override.yaml # result
sites: sites: sites:
- name: docs - name: docs - name: docs
max_bytes: 10 max_bytes: 20 max_bytes: 20
- name: wiki - name: wiki
max_bytes: 10 max_bytes: 10
Elements then bind to "sites[name=docs].max_bytes", which is a stable identity across layers where a position is not: prepending one element would otherwise re-target every override silently. Fields merge individually, so a later layer changes only what it names, and "sites[name=docs]: null" removes the element outright.
The key field becomes mandatory in every element, and repeating it within one layer is an error rather than last-wins. Base order is preserved and unseen keys are appended, so a later layer cannot reorder: do not key a list whose order is meaningful.
func (*ListField) MergeReplace ¶ added in v0.3.0
MergeReplace takes the list from the last layer that provided one. It is the default.
type MapField ¶ added in v0.3.0
type MapField struct{ *FieldBuilder }
MapField is the fluent builder for a map of objects.
func Map ¶ added in v0.3.0
func Map[R any, K comparable, E any]( s *Schema[R], field *map[K]E, name string, d *Descriptor[E], opts ...FieldOption, ) *MapField
Map registers a map whose values are described by their own descriptor. The pointer is the binding identity: figureout resolves a registration by the address of the field inside the synthetic root, so it cannot take the map itself.
func MapOf ¶ added in v0.3.0
func MapOf[R any, K comparable, E any]( s *Schema[R], field *map[K]E, name string, describe func(*E, *Schema[E]), opts ...FieldOption, ) *MapField
MapOf registers a map whose values are configuration objects, described inline. The map key identifies an element, so "proxies[gitlab].url" names one entry. The pointer is the binding identity: figureout resolves a registration by the address of the field inside the synthetic root, so it cannot take the map itself.
func (*MapField) MergeByKey ¶ added in v0.3.0
MergeByKey merges entries across layers, so a later layer changes only the entries it names, and only the fields it names within them.
figureout.MapOf(s, &c.Proxies, "proxies", describeProxy).MergeByKey()
An entry already identifies itself, so no key has to be named. Setting an entry to null removes it.
func (*MapField) MergeReplace ¶ added in v0.3.0
MergeReplace takes the map from the last layer that provided one. It is the default, as it is everywhere else: a predictable last-one-wins is what a reader of a layered configuration can reason about.
type MergePolicy ¶
type MergePolicy uint8
MergePolicy decides how a field combines values from several layers.
The default is MergeReplace, because a predictable "last one wins" is what a reader of a layered configuration can reason about. The other policies exist for the collections where accumulating across layers is the point.
const ( // MergeReplace takes the value from the last layer that provided one. MergeReplace MergePolicy = iota // MergeAppend concatenates list values across layers, in layer order. MergeAppend // MergeByKey merges map entries across layers, so a later layer changes // only the keys it names. MergeByKey )
Merge policies.
func (MergePolicy) Applies ¶
func (p MergePolicy) Applies(kind TypeKind) bool
Applies reports whether the policy is meaningful for kind.
type Metadata ¶
type Metadata struct {
Doc string
Deprecated string
Hidden bool
// Secret marks a credential. Unlike Hidden it is enforced: see [Secret].
Secret bool
Examples []any
}
Metadata is documentation attached to a field.
type Model ¶
type Model struct {
Root *ObjectModel
// contains filtered or unexported fields
}
Model is the compiled, format-neutral descriptor model.
It contains no JSON Schema, CUE or wire types: emitters project it.
func (*Model) FieldByPath ¶
func (m *Model) FieldByPath(path string) (*FieldModel, bool)
FieldByPath looks up a field by canonical dotted path.
A concrete element path resolves to the field describing every element, so "sites[0].max_bytes" and "sites[name=docs].max_bytes" both find "sites[].max_bytes".
func (*Model) Fields ¶
func (m *Model) Fields() []*FieldModel
Fields returns every field in the model, including nested ones, in declaration order.
func (*Model) Invariants ¶ added in v0.2.0
func (m *Model) Invariants() []InvariantModel
Invariants returns the cross-field rules the descriptor declares.
type ObjectField ¶
type ObjectField struct{ *FieldBuilder }
ObjectField is a fluent builder for nested object fields.
func Group ¶ added in v0.2.0
func Group[T any](s *Schema[T], name string, describe func(*Schema[T]), opts ...FieldOption) *ObjectField
Group opens a configuration path level that has no Go struct behind it.
The shape that reads well in a file and the shape a consumer wants in Go are not always the same shape. A group registers flat Go fields under a nested path, so neither side has to be reshaped to match the other:
figureout.Group(s, "webhook", func(s *figureout.Schema[GitLab]) {
figureout.Value(s, &c.WebhookEnabled, "enabled").ApplyDefault(false)
figureout.Value(s, &c.WebhookSecret, "secret", figureout.Hidden())
})
webhook:
enabled: true
secret: hunter2
Only the path nests: the fields still bind to the same struct, so the completeness and duplicate-registration checks see exactly the fields they would have seen without the group. A group contributes a segment everywhere a nested object would, including environment variable names (GITLAB_WEBHOOK_SECRET) and generated schemas.
Registering a nested Go struct's fields as siblings of the parent — the inverse mismatch — needs no dedicated function: register the descendants directly, as in figureout.Value(s, &c.Database.DSN, "dsn").
func Object ¶
func Object[R, F, C any](s *Schema[R], field *F, name string, d *Descriptor[C], opts ...FieldOption) *ObjectField
Object registers a nested configuration object described by its own descriptor.
func ObjectFunc ¶ added in v0.2.0
func ObjectFunc[R, F, C any]( s *Schema[R], field *F, name string, describe func(*C, *Schema[C]), opts ...FieldOption, ) *ObjectField
ObjectFunc registers a nested configuration object described inline.
It is Object without a descriptor variable: describe runs against a nested Schema rooted at the field, so pointer binding, completeness and name collisions are scoped to C exactly as they would be in a separate Derive.
figureout.ObjectFunc(s, &c.Server, "server", func(c *Server, s *figureout.Schema[Server]) {
figureout.Explicit(s, &c.Port, "port").InRange(1, 65535)
})
Prefer Object for a descriptor shared by several parents or exported for its own sake, and ObjectFunc for a section that has exactly one parent.
func OptionalObject ¶ added in v0.7.0
func OptionalObject[R, F, C any]( s *Schema[R], field *F, name string, d *Descriptor[C], opts ...FieldOption, ) *ObjectField
OptionalObject registers a nested object a source may leave out entirely.
An optional section distinguishes what a zero struct cannot: "there is no cluster" is not "there is a cluster and every one of its fields defaulted". The carrier is unset unless some source contained the section, and a section that was contained is materialized even when every member of it defaulted.
Any of the three optional carriers holds one:
S3 figureout.OptionalOf[S3Config] // the carrier a new configuration wants S3 figureout.OptionalOf[*S3Config] // the same, held behind a pointer S3 *S3Config // the shape an adopted struct already has figureout.OptionalObject(s, &c.Storage.S3, "s3", s3Descriptor)
A member registered with Explicit is demanded only where the section is present, which is what makes "required inside an optional section" mean something. An explicit null erases the section, along with whatever earlier layers put in it.
func OptionalObjectFunc ¶ added in v0.7.0
func OptionalObjectFunc[R, F, C any]( s *Schema[R], field *F, name string, describe func(*C, *Schema[C]), opts ...FieldOption, ) *ObjectField
OptionalObjectFunc registers a nested object a source may leave out, described inline.
It is OptionalObject without a descriptor variable, exactly as ObjectFunc is Object without one.
func ScalarOr ¶ added in v0.2.0
func ScalarOr[R, C, S any]( s *Schema[R], field *C, name string, d *Descriptor[C], widen func(S) C, opts ...FieldOption, ) *ObjectField
ScalarOr registers a nested object that may also be written as a scalar.
"A scalar, or an object" is one of the most common configuration idioms, and OneOf cannot express it: a union needs a discriminator, and a bare scalar has nowhere to put one. Without ScalarOr every occurrence is a WithDecoder plus hand-written Shape values that duplicate the descriptor already describing the same thing, and drift the moment a field is added to it.
figureout.ScalarOr(s, &c.AuthToken, "auth_token", secretDescriptor,
func(v string) Secret { return Secret{Value: v} })
auth_token: sk-live-... # widened by the function
auth_token: {file: /run/token} # decoded by the descriptor
The accepted shapes are derived from the descriptor and from S, so they cannot drift: generated schemas emit oneOf over the two, and a source that has no object syntax — environment variables, mounted files — accepts the scalar spelling directly at the object's own name.
A scalar and an object spelling never combine across layers: whichever a later layer uses replaces the other outright, because a widened value and a half-filled object have no meaningful merge.
func (*ObjectField) Doc ¶
func (f *ObjectField) Doc(text string) *ObjectField
Doc attaches documentation.
type ObjectModel ¶
type ObjectModel struct {
// Go is the struct type described.
Go reflect.Type
Fields []*FieldModel
// contains filtered or unexported fields
}
ObjectModel is a compiled configuration object.
func (*ObjectModel) Field ¶
func (o *ObjectModel) Field(name string) (*FieldModel, bool)
Field looks up a field by its canonical name within the object.
type OpaqueField ¶ added in v0.7.0
type OpaqueField struct{ *FieldBuilder }
OpaqueField is the fluent builder for a passthrough subtree.
It carries no constraints and no defaults: there is nothing described to constrain, and a default would be this program's opinion about another one's configuration.
func Opaque ¶ added in v0.7.0
func Opaque[R, C any](s *Schema[R], field *C, name string, opts ...FieldOption) *OpaqueField
Opaque registers a subtree carried verbatim, whose shape belongs to another program.
A configuration that embeds another program's configuration has a block it cannot describe and must not validate:
// an OpenTelemetry Collector configuration, handed to the collector as-is
Collector map[string]any `yaml:"otelcol"`
figureout.Opaque(s, &c.Collector, "otelcol",
figureout.Reason("handed to the collector verbatim"))
The field decodes to whatever the document held — objects as map[string]any, arrays as []any, scalars as the format resolved them — and its whole subtree is exempt from [DisallowUnknownFields]. That exemption is the load-bearing part: strict decoding is why a descriptor is worth adopting, and a passthrough is precisely where strictness has to stop, because figureout cannot know which keys the other program accepts and a version skew in *that* program is not this one's business.
It is therefore a deliberate hole, and Reason is required so that it reads as one at the declaration site. The reason is documentation: generated schemas describe a permissive object carrying it, rather than omitting the field.
Absence resolves to the zero value, as Value does; FieldBuilder.Required opts back in. A source with no nesting, such as environment variables or mounted files, skips the field the way it skips a collection of objects.
Opaque is not an escape hatch for a block that could be described. Whatever is inside it has no names, no constraints, no defaults, no provenance and no schema — reach for ObjectFunc wherever the shape is yours to state.
func (*OpaqueField) Doc ¶ added in v0.7.0
func (f *OpaqueField) Doc(text string) *OpaqueField
Doc attaches documentation.
func (*OpaqueField) Required ¶ added in v0.7.0
func (f *OpaqueField) Required() *OpaqueField
Required makes an absent passthrough an error instead of the zero value.
type OptionalOf ¶
type OptionalOf[T any] struct { // contains filtered or unexported fields }
OptionalOf represents a value that is either missing or present.
Unlike a pointer, it carries no aliasing and distinguishes "not provided by any source" from "provided as the zero value".
There is no nullable counterpart: an explicit null in a source erases what earlier layers set rather than becoming a value the field holds.
func (*OptionalOf[T]) Clear ¶
func (o *OptionalOf[T]) Clear()
Clear makes the value missing and resets it to the zero value.
func (OptionalOf[T]) IsSet ¶
func (o OptionalOf[T]) IsSet() bool
IsSet reports whether a value is present.
func (OptionalOf[T]) MarshalJSON ¶ added in v0.7.0
func (o OptionalOf[T]) MarshalJSON() ([]byte, error)
MarshalJSON implements json.Marshaler.
func (OptionalOf[T]) MarshalYAML ¶ added in v0.7.0
func (o OptionalOf[T]) MarshalYAML() (any, error)
MarshalYAML implements yaml.Marshaler.
func (OptionalOf[T]) OrElse ¶
func (o OptionalOf[T]) OrElse(v T) T
OrElse returns the value if present, otherwise v.
func (OptionalOf[T]) String ¶
func (o OptionalOf[T]) String() string
String implements fmt.Stringer.
func (*OptionalOf[T]) UnmarshalJSON ¶ added in v0.7.0
func (o *OptionalOf[T]) UnmarshalJSON(data []byte) error
UnmarshalJSON implements json.Unmarshaler.
func (*OptionalOf[T]) UnmarshalYAML ¶ added in v0.7.0
func (o *OptionalOf[T]) UnmarshalYAML(n *yaml.Node) error
UnmarshalYAML implements yaml.Unmarshaler.
func (OptionalOf[T]) Value ¶
func (o OptionalOf[T]) Value() (T, bool)
Value returns the value and whether it is present.
type Origin ¶
type Origin struct {
Source SourceID
// Name is the source-specific name, such as an environment variable.
Name string
File string
Line int
Col int
}
Origin records where a value came from.
type PatternConstraint ¶
type PatternConstraint struct {
Expression string
Dialect PatternDialect
// contains filtered or unexported fields
}
PatternConstraint restricts a string to a regular expression.
func (PatternConstraint) Applies ¶
func (PatternConstraint) Applies(kind TypeKind) bool
Applies implements Constraint.
func (PatternConstraint) Validate ¶
func (c PatternConstraint) Validate(v any) error
Validate implements Constraint.
type PatternDialect ¶
type PatternDialect uint8
PatternDialect names the regular expression syntax of a PatternConstraint.
const ( // PatternRE2 is Go's [regexp] syntax. PatternRE2 PatternDialect = iota // PatternECMA is the syntax used by JSON Schema. PatternECMA )
Pattern dialects.
type Presence ¶
type Presence uint8
Presence describes how a field models the absence of a value.
const ( // PresenceRequired is a plain Go value: it is always materialized, and a // missing value is an error unless a default applies. PresenceRequired Presence = iota // PresenceOptional is an [OptionalOf] carrier: missing or present. // // There is deliberately no nullable state. An explicit null in a source is // a merge directive that erases earlier layers, not a value a field holds, // so nullability never reaches the Go type. PresenceOptional )
Presence values.
type RangeConstraint ¶
RangeConstraint bounds a numeric or duration value.
func (RangeConstraint) Applies ¶
func (RangeConstraint) Applies(kind TypeKind) bool
Applies implements Constraint.
func (RangeConstraint) Validate ¶
func (c RangeConstraint) Validate(v any) error
Validate implements Constraint.
type ReasonOption ¶ added in v0.7.0
type ReasonOption struct {
// contains filtered or unexported fields
}
ReasonOption is Reason. It is both an IgnoreOption and a FieldOption because both kinds of declaration are a decision not to describe something.
func Reason ¶
func Reason(text string) ReasonOption
Reason documents why a field is not described.
It applies to Ignore, where it says why a Go field is not configuration, and to Opaque, where it is required: a passthrough takes its whole subtree out of unknown-field checking, and a hole in strictness has to read as one at the declaration site.
func (ReasonOption) ApplyFieldOption ¶ added in v0.7.0
func (o ReasonOption) ApplyFieldOption(c FieldOptionContext) error
ApplyFieldOption implements FieldOption.
type Report ¶
type Report struct {
Diagnostics Diagnostics
// contains filtered or unexported fields
}
Report describes how a configuration was resolved.
func (*Report) ErasedBy ¶
ErasedBy returns the layer that erased the value at the canonical path.
A source spells an erase as an explicit null; the field then falls back to its default, or stays missing.
func (*Report) Secret ¶ added in v0.2.0
Secret reports whether the value at the canonical path is a credential.
A consumer walking Report.Origins to log where its configuration came from can use it to keep the values it prints alongside them out of the log.
type Schema ¶
type Schema[T any] struct { // contains filtered or unexported fields }
Schema is the mutable registration builder for T.
It is single-use and not safe for concurrent use; the compiled Descriptor is immutable and safe for concurrent use.
func (*Schema[T]) Diagnostics ¶
func (s *Schema[T]) Diagnostics() Diagnostics
Diagnostics returns the diagnostics recorded so far.
type SchemaOption ¶
type SchemaOption interface {
// contains filtered or unexported methods
}
SchemaOption customizes descriptor construction.
func Completeness ¶
func Completeness(m CompletenessMode) SchemaOption
Completeness selects the completeness mode. The default is CompletenessExported.
func Tag ¶
func Tag(name string) SchemaOption
Tag sets the struct tag consulted by CompletenessTagged. The default is "config".
func WithTypeRegistry ¶
func WithTypeRegistry(r *TypeRegistry) SchemaOption
WithTypeRegistry supplies named type descriptions to descriptor construction.
type Section ¶ added in v0.7.0
type Section struct{}
Section is the value a source assigns to an optional object itself, to say that this layer contained the section.
A section that is there and defaulted throughout has no member assignment to speak for it, and a nil pointer has to keep meaning "no section": without a marker of its own, "s3: {}" and no "s3" key at all would resolve alike. It is Collection and Element one level up, for the same reason.
A source with no nesting has nothing to write it with, so an optional section is present there whenever anything under it is. See [Model.sectionPresent].
type ShapeKind ¶
type ShapeKind uint8
ShapeKind is a wire-level representation kind.
It describes what a source accepts, not what the value means; see TypeKind for semantics.
type SourceID ¶
type SourceID string
SourceID identifies an input mechanism such as JSON or environment variables.
type SourceNamer ¶ added in v0.6.0
type SourceNamer interface {
Source
// ProjectNames maps a canonical field path to the names this source
// accepts for it, primary first. A field the source cannot read, such as a
// list of objects in the environment, is absent from the result.
ProjectNames(m *Model) map[string][]string
}
SourceNamer reports the names a source accepts for every field.
A name belongs to the source rather than to the model: the environment source joins the segments of a path, applies its own naming and prepends the prefix the caller configured, so only that source knows "server.port" is read from APP_SERVER_LISTEN_PORT. A target that documents names asks the source for them instead of re-deriving them, which is what keeps documentation from drifting from what is actually read.
A configured source is therefore the unit that can answer, not a SourceID.
type SourceProjection ¶
type SourceProjection struct {
Source SourceID
// Names lists the accepted names, primary first, aliases after.
Names []string
// Accepts lists the wire shapes the source accepts for this field. When
// empty, the shape is derived from the semantic type.
Accepts []Shape
// Skip excludes the field from this source entirely.
Skip bool
Decoder Decoder
Encoder Encoder
// Options carries source-specific settings, owned by the source package.
Options []any
}
SourceProjection is how one field is represented and decoded by one source.
func (*SourceProjection) DeriveShapes ¶
func (p *SourceProjection) DeriveShapes(t Type) []Shape
DeriveShapes returns the accepted shapes, falling back to the shape implied by the semantic type when the source declares none.
func (*SourceProjection) Name ¶
func (p *SourceProjection) Name() string
Name returns the primary name of the projection.
type TargetID ¶
type TargetID string
TargetID identifies an output representation such as JSON Schema or CUE.
type Type ¶
type Type struct {
Kind TypeKind
// Go is the Go type carrying the value, with [OptionalOf] already
// unwrapped.
Go reflect.Type
// Unit scales a bare number written for a [TypeDuration] field, so that
// "timeout_seconds: 180" resolves to 180 * time.Second. Zero means the
// field is only spelled as a duration. See [Unit].
Unit time.Duration
Elem *Type // list element, map value
Key *Type // map key
Object *ObjectModel // object fields
Union *Union // union variants
// Scalar is the scalar spelling an object also accepts, as set by
// [ScalarOr]. It is nil for an object that is only ever written as one.
Scalar *Type
// Text reports that the Go type parses itself from text through
// [encoding.TextUnmarshaler], which then decides what every spelling
// means. See [textScalar].
Text bool
}
Type is a semantic type. It never refers to a wire format; see SourceProjection for the representation accepted by a given source.
type TypeKind ¶
type TypeKind uint8
TypeKind is the format-neutral meaning of a value.
type TypeOption ¶
type TypeOption interface {
// contains filtered or unexported methods
}
TypeOption describes a registered type.
func Constrain ¶
func Constrain(c Constraint) TypeOption
Constrain attaches a constraint to every field of the type.
func InRange ¶
func InRange(minimum, maximum any) TypeOption
InRange constrains every field of the type to an inclusive range.
func TypeFieldOptions ¶
func TypeFieldOptions(opts ...FieldOption) TypeOption
TypeFieldOptions applies field options to every field of the type, such as source decoders or accepted shapes.
type TypeRegistry ¶
type TypeRegistry struct {
// contains filtered or unexported fields
}
TypeRegistry describes named domain types once, so that every field of that type inherits the same semantics, constraints and source options.
Registries are explicit: there is no global registration. A registry must not be modified after it has been passed to Derive.
func NewTypeRegistry ¶
func NewTypeRegistry() *TypeRegistry
NewTypeRegistry returns an empty registry.
type Union ¶
type Union struct {
// Discriminator is the property carrying the variant tag.
Discriminator string
Variants []*VariantModel
}
Union is a tagged sum of object variants.
A union is distinct from an enumeration: an enum constrains a scalar to a set of values, while a union selects between alternative shapes.
type UnionField ¶
type UnionField struct{ *FieldBuilder }
UnionField is a fluent builder for union fields.
func OneOf ¶
func OneOf[R, C any](s *Schema[R], field *C, name string, opts ...UnionOption) *UnionField
OneOf registers a tagged union: a field whose shape is selected by a discriminator property.
A union is a sum of alternative shapes. To restrict one scalar to a set of values, use Enum instead.
figureout.OneOf(s, &c.Backend, "backend",
figureout.Discriminator("type"),
figureout.Variant("s3", &c.Backend.S3, S3Descriptor),
figureout.Variant("local", &c.Backend.Local, LocalDescriptor),
)
func (*UnionField) Doc ¶
func (f *UnionField) Doc(text string) *UnionField
Doc attaches documentation.
type UnionOption ¶
type UnionOption interface {
// contains filtered or unexported methods
}
UnionOption declares part of a union.
func Discriminator ¶
func Discriminator(name string) UnionOption
Discriminator names the property carrying the variant tag. It is required.
func Variant ¶
func Variant[V any](tag string, field **V, d *Descriptor[V]) UnionOption
Variant declares one alternative of a union.
The variant field must be a pointer to the variant struct: the pointer being non-nil is what records which variant was selected.
type ValueField ¶
type ValueField[T any] struct { *FieldBuilder }
ValueField is the fluent builder for a value field carrying T.
Constraints are typed: InRange takes two T rather than two any, so a bound that does not belong to the field is a compile error rather than a descriptor diagnostic. Constraints that do not apply to the field's semantic kind, such as MinLength on an integer, are still rejected during compilation.
func Enum ¶
func Enum[R any, T EnumValuer[T]](s *Schema[R], field *T, name string, opts ...FieldOption) *ValueField[T]
Enum registers a field whose type enumerates its own values.
An enum is a set of allowed values for one type. It is distinct from OneOf, which selects between alternative shapes.
An enum field is required, as Explicit is: the zero value of an enumerated type is rarely one of its members, so absence needs a default rather than a fallback nobody declared.
func EnumFunc ¶
func EnumFunc[R, T any](s *Schema[R], field *T, name string, values func() []T, opts ...FieldOption) *ValueField[T]
EnumFunc registers an enumerated field whose values come from a function.
Generators that emit a package-level function rather than a method, such as enumer's LogLevelValues, are registered this way.
func EnumSlice ¶
func EnumSlice[R any, T EnumSliceValuer[T]](s *Schema[R], field *T, name string, opts ...FieldOption) *ValueField[T]
EnumSlice registers a field whose type enumerates its own values through a Values method.
func EnumValues ¶
func EnumValues[R, T any](s *Schema[R], field *T, name string, values []T, opts ...FieldOption) *ValueField[T]
EnumValues registers an enumerated field with an explicit set of values.
func Explicit ¶ added in v0.5.0
func Explicit[R, T any](s *Schema[R], field *T, name string, opts ...FieldOption) *ValueField[T]
Explicit registers a plain field some source has to provide.
It is Value without the zero fallback: a missing value is an error unless the field carries an applied default. Use it for what an operator has to decide, such as a database address or a listen port.
A collection is required too, rather than resolving to an empty one: the absent-is-empty rule is what a collection does when nobody says otherwise, and Explicit says otherwise.
func Optional ¶
func Optional[R, T any](s *Schema[R], field *OptionalOf[T], name string, opts ...FieldOption) *ValueField[T]
Optional registers a field that a source may leave out.
The element type is inferred from the carrier, so the builder and its constraints are typed as T rather than as OptionalOf[T].
func Value ¶
func Value[R, T any](s *Schema[R], field *T, name string, opts ...FieldOption) *ValueField[T]
Value registers a plain field whose absence resolves to the zero value of T.
The semantic type is derived from T, so named types such as "type Port uint16" are integers with whatever the type registry adds.
Absence is not an error: a field nobody configured reads as "", 0 or false, which is what an optional scalar with no meaningful default wants, and the zero value is already visible in the Go type. Say so differently when it is not what the field means: Explicit demands a value, ValueField.ApplyDefault substitutes another one, and Optional keeps absence visible to the consumer.
A zero that no source could have written is a compilation error rather than a silent one, so a field constrained by NonEmpty, InRange or Enum cannot fall back to it.
func (*ValueField[T]) ApplyDefault ¶
func (f *ValueField[T]) ApplyDefault(v T) *ValueField[T]
ApplyDefault sets a default that changes the resolved value when no source provides one. For an OptionalOf field this makes the value present.
func (*ValueField[T]) AtLeast ¶
func (f *ValueField[T]) AtLeast(minimum T) *ValueField[T]
AtLeast sets an inclusive lower bound.
func (*ValueField[T]) AtMost ¶
func (f *ValueField[T]) AtMost(maximum T) *ValueField[T]
AtMost sets an inclusive upper bound.
func (*ValueField[T]) Check ¶
func (f *ValueField[T]) Check(name string, fn func(T) error) *ValueField[T]
Check adds an opaque runtime validator. Opaque validators never contribute to generated schemas.
func (*ValueField[T]) Deprecated ¶
func (f *ValueField[T]) Deprecated(reason string) *ValueField[T]
Deprecated marks the field as deprecated.
func (*ValueField[T]) Doc ¶
func (f *ValueField[T]) Doc(text string) *ValueField[T]
Doc attaches documentation.
func (*ValueField[T]) DocumentDefault ¶
func (f *ValueField[T]) DocumentDefault(v T) *ValueField[T]
DocumentDefault records a default for documentation only, without changing resolution.
func (*ValueField[T]) Enum ¶
func (f *ValueField[T]) Enum(values ...T) *ValueField[T]
Enum restricts the field to a set of values.
Prefer Enum or EnumSlice when the type enumerates its own values, so that the value set has a single source of truth.
func (*ValueField[T]) Examples ¶
func (f *ValueField[T]) Examples(values ...T) *ValueField[T]
Examples attaches example values.
func (*ValueField[T]) GreaterThan ¶
func (f *ValueField[T]) GreaterThan(minimum T) *ValueField[T]
GreaterThan sets an exclusive lower bound.
func (*ValueField[T]) Hidden ¶
func (f *ValueField[T]) Hidden() *ValueField[T]
Hidden hides the field from generated documentation.
func (*ValueField[T]) InRange ¶
func (f *ValueField[T]) InRange(minimum, maximum T) *ValueField[T]
InRange bounds the value inclusively.
func (*ValueField[T]) LessThan ¶
func (f *ValueField[T]) LessThan(maximum T) *ValueField[T]
LessThan sets an exclusive upper bound.
func (*ValueField[T]) MaxItems ¶
func (f *ValueField[T]) MaxItems(n uint64) *ValueField[T]
MaxItems allows at most n elements.
func (*ValueField[T]) MaxLength ¶
func (f *ValueField[T]) MaxLength(n uint64) *ValueField[T]
MaxLength allows at most n characters, bytes or elements.
func (*ValueField[T]) MergeAppend ¶
func (f *ValueField[T]) MergeAppend() *ValueField[T]
MergeAppend concatenates list values across layers, in layer order.
func (*ValueField[T]) MergeByKey ¶
func (f *ValueField[T]) MergeByKey() *ValueField[T]
MergeByKey merges map entries across layers, so a later layer changes only the keys it names.
func (*ValueField[T]) MergeReplace ¶
func (f *ValueField[T]) MergeReplace() *ValueField[T]
MergeReplace takes the value from the last layer that provided one. It is the default.
func (*ValueField[T]) MinItems ¶
func (f *ValueField[T]) MinItems(n uint64) *ValueField[T]
MinItems requires at least n elements.
func (*ValueField[T]) MinLength ¶
func (f *ValueField[T]) MinLength(n uint64) *ValueField[T]
MinLength requires at least n characters, bytes or elements.
func (*ValueField[T]) NonEmpty ¶
func (f *ValueField[T]) NonEmpty() *ValueField[T]
NonEmpty requires a length of at least one.
func (*ValueField[T]) Pattern ¶
func (f *ValueField[T]) Pattern(expr string) *ValueField[T]
Pattern requires the value to match an RE2 regular expression.
func (*ValueField[T]) Required ¶ added in v0.4.0
func (f *ValueField[T]) Required() *ValueField[T]
Required makes an absent collection an error instead of an empty one.
func (*ValueField[T]) With ¶
func (f *ValueField[T]) With(opts ...FieldOption) *ValueField[T]
With applies field options after registration.
type ValueState ¶
type ValueState uint8
ValueState distinguishes a missing value from an explicit null.
const ( ValueMissing ValueState = iota ValueNull ValuePresent )
Value states.
type VariantModel ¶
type VariantModel struct {
// Tag is the discriminator value selecting this variant.
Tag string
// GoPath locates the variant field inside the union container.
GoPath FieldPath
// Object describes the variant payload.
Object *ObjectModel
// contains filtered or unexported fields
}
VariantModel is one alternative of a Union.
type Violation ¶ added in v0.2.0
type Violation struct {
// contains filtered or unexported fields
}
Violation builds an error naming the paths a cross-field rule is about.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
examples
|
|
|
service
command
Command service shows figureout in a realistic setup: a layered configuration, provenance in its output, and a JSON Schema generated from the same definition.
|
Command service shows figureout in a realistic setup: a layered configuration, provenance in its output, and a JSON Schema generated from the same definition. |
|
internal
|
|
|
integration
Package integration checks that the pieces agree on one realistic configuration: what the sources decode, and what the generated schema accepts, are the same document.
|
Package integration checks that the pieces agree on one realistic configuration: what the sources decode, and what the generated schema accepts, are the same document. |
|
scalar
Package scalar parses textual scalars into semantic configuration values.
|
Package scalar parses textual scalars into semantic configuration values. |
|
tree
Package tree is the shared document model for hierarchical sources.
|
Package tree is the shared document model for hierarchical sources. |
|
schema
|
|
|
docs
Package docs emits reference documentation from a configuration descriptor, so that the documented configuration cannot drift from the decoded one.
|
Package docs emits reference documentation from a configuration descriptor, so that the documented configuration cannot drift from the decoded one. |
|
jsonschema
Package jsonschema emits JSON Schema from a configuration descriptor.
|
Package jsonschema emits JSON Schema from a configuration descriptor. |
|
source
|
|
|
env
Package env projects a configuration descriptor onto environment variables.
|
Package env projects a configuration descriptor onto environment variables. |
|
file
Package file reads configuration values from a directory of files, one value per file.
|
Package file reads configuration values from a directory of files, one value per file. |
|
json
Package json reads configuration from JSON documents.
|
Package json reads configuration from JSON documents. |
|
yaml
Package yaml reads configuration from YAML documents.
|
Package yaml reads configuration from YAML documents. |