schema

package
v0.0.0-...-3d40d98 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package schema lowers OpenAPI schemas into IR types: the shape walk itself, the compositions written around it, the references that reach other schemas, and the preservation of what the IR has no field for.

It is one package because those are one cycle. Lowering a schema resolves the references inside it, resolving a reference lowers what it names, and a composition is lowered by lowering its branches — so no line can be drawn through the set that some call does not cross back over (micro-compiler-design §5). The mutual recursion is pinned, by name, in internal/archtest.

Its exported surface is the entry points the rest of the compiler needs, plus the few facts a carrier lowering has to agree with this one about. Everything the walk says to itself stays unexported, so a caller cannot enter it halfway down — which is why the recursion pinned in internal/archtest is almost entirely unexported names.

Index

Constants

View Source
const TopLevelDepth = 0

TopLevelDepth is the nesting a schema position outside the walk starts at. The walk counts its own frames, so every entry point into it begins at zero; naming it keeps a bare 0 out of the call sites that only pass it through.

Variables

This section is empty.

Functions

func AppendExample

func AppendExample(c lowering.Ctx, out []ir.Example, proto ir.Example, node *yaml.Node,
	base string, seg ...string,
) ([]ir.Example, []ir.Diagnostic)

AppendExample converts node into proto's value and appends the result to out; an unconvertible node is skipped and yields a warning diagnostic for the caller to record, rather than being silently dropped — an example is an annotation, not a structural hole, so losing it is fine as long as it isn't silent. proto carries the annotations that surround the value (name, summary, description); base and seg locate the node, joined into a pointer only on the failure path, so an example that converts builds no pointer string at all. Shared by every example site: schema (schemaExamples), media type, header, and parameter (exampleList).

func CarriedRef

func CarriedRef(c lowering.Ctx, ts *compile.Types, anchors *AnchorIndex, depth int, js *oas3.JSONSchema[oas3.Referenceable], pointer, hint string) (ir.TypeRef, []ir.Diagnostic)

CarriedRef lowers a schema whose annotations the calling position already carries — a model property, a header, a parameter. Those callers copy the declaration onto their own ir.Property/ir.Parameter, so the pointer must not also hoist a node to hold it: one home per declaration.

func ExtensionsIn

func ExtensionsIn(c lowering.Ctx, ext *extensions.Extensions, owner, scope string) (ir.Unmodeled, []ir.Diagnostic)

ExtensionsIn is ExtensionsOf for an object with no Unmodeled map of its own, whose entries ride on an enclosing node's under scope — see annotation.ExtensionsUnder for what scope names and why it is needed.

func ExtensionsOf

func ExtensionsOf(c lowering.Ctx, ext *extensions.Extensions, owner string) (ir.Unmodeled, []ir.Diagnostic)

ExtensionsOf lowers ext's x-* extensions into namespaced Unmodeled, returning the entries and any serialization-failure diagnostics separately. It is named for what it returns rather than for the construct, because this package also imports the extensions library.

The two returns are independent on purpose, and a caller must record the diagnostics whether or not it keeps the entries: gating the append behind the same "len(ext) > 0" that guards the assignment drops every warning on an object whose extensions all failed to serialize — exactly when the result is empty. TestOperation_UnserializableExtensionStillWarns and its security-scheme twin hold two of the callers to that.

func FillPropertyDetail

func FillPropertyDetail(c lowering.Ctx, ts *compile.Types, anchors *AnchorIndex, p *ir.Property, js *oas3.JSONSchema[oas3.Referenceable], pointer string) []ir.Diagnostic

FillPropertyDetail enriches a property from its schema: the property-scoped facts a type node has no field for (default, visibility, secrecy, constraints), then the declaration's annotations. Annotations present at a $ref use-site override the target's (ir-design §14).

Constraints stay unconditional: ir.Property is the only home every property has. Some nodes a property's schema can hoist carry the same bounds (a Model, and the Scalar the content vocabulary hoists) and some carry none (the Scalar a byte or unknown format hoists, or no node at all), so reading the node instead would drop them wherever it carries none. Restating them is the safe half of that trade.

func LowerComponentSchemas

func LowerComponentSchemas(ctx context.Context, c lowering.Ctx, ts *compile.Types, anchors *AnchorIndex) []ir.Diagnostic

LowerComponentSchemas interns every named component schema in source order. It is the entry Compile's run() calls before any operation lowering so that $refs resolve to already-registered IDs.

ctx bounds the walk in time: a document declares as many components as it likes, so this loop is one of the two places a compile does work proportional to nothing the compiler chose. Cancellation stops it between components and returns what was lowered so far; the caller — run — sees ctx.Err() at the phase boundary immediately after and refuses the document there, so a partial registry never becomes a Document.

func LoweredToOwnNode

func LoweredToOwnNode(ts *compile.Types, pointer string, t ir.TypeRef) bool

LoweredToOwnNode reports whether the declaration at pointer lowered to a type node of its own — the node attachDeclaredAnnotations then fills, leaving its carrier nothing to hold.

It asks whether the node interned at pointer is the one the declaration lowered to, not merely whether a node is interned there. A $ref naming an inline position hoists that position's home for its own use, in either declaration order, and a carrier reading the registry alone would keep its schema's annotations only when it happened to lower first.

func Preserve

func Preserve(c lowering.Ctx, p *ir.Unmodeled, key string, raw ir.RawValue,
	reason ir.UnmodeledReason, pointer string,
)

Preserve records raw under key in *p with why it was kept and where it was written, allocating the map on first write. An absent or unconvertible payload records nothing, so no caller needs a nil guard of its own.

func PreserveNode

func PreserveNode(c lowering.Ctx, p *ir.Unmodeled, key string, node *yaml.Node,
	reason ir.UnmodeledReason, pointer string,
) (bool, []ir.Diagnostic)

PreserveNode records the construct written at node under key in *p, reporting one that could not be converted at all. It returns whether an entry was written, so a caller announces only what it actually kept (GitHub #144).

func PreserveRefSiteKeywords

func PreserveRefSiteKeywords(c lowering.Ctx, ts *compile.Types, p *ir.Unmodeled,
	js *oas3.JSONSchema[oas3.Referenceable], t ir.TypeRef, pointer string,
) []ir.Diagnostic

PreserveRefSiteKeywords keeps, on a carrier's own Unmodeled, the keywords a $ref position declared that the alias it resolves to cannot hold. It is refSiteRef's other half: the same census, recorded where an annotation.HomeCarrier position keeps everything else its schema declared.

A schema that lowered to a node of its own already had the census recorded there, so the carrier adds nothing — one home per declaration, exactly as fillPropertyAnnotations draws the line.

func PreserveSchemaKeyword

func PreserveSchemaKeyword(c lowering.Ctx, p *ir.Unmodeled, s *oas3.Schema, keyword string,
	reason ir.UnmodeledReason, pointer string,
) (bool, []ir.Diagnostic)

PreserveSchemaKeyword records the top-level keyword s writes under key. It is PreserveNode addressed by keyword rather than by node, which is how all but a handful of preservation sites reach their payload.

func PreserveUnknownKeywords

func PreserveUnknownKeywords(c lowering.Ctx, p *ir.Unmodeled, s *oas3.Schema, pointer string) []ir.Diagnostic

PreserveUnknownKeywords records every keyword s writes that no field of the schema model names and no reader above it already kept (GitHub #297).

It is the last thing an attachment does, which is the contract annotation.UnknownKeywordsIn states and the reason it is called here rather than from inside annotation.Read with the other readers: $dynamicRef has no field in the schema model either and is decided by recordUnexpandedDynamicRef, which runs after Read and deliberately keeps nothing once the reference has expanded. A census running before it could not tell that from an unread keyword.

func Ref

func Ref(c lowering.Ctx, ts *compile.Types, anchors *AnchorIndex, depth int, js *oas3.JSONSchema[oas3.Referenceable], pointer, hint string) (ir.TypeRef, []ir.Diagnostic)

Ref is THE schema entry point: every schema position (property, items, params, bodies) flows through it, yielding a TypeRef into the type registry. It normalizes the two nullability dialects onto the single IR bit and never lowers a $ref target from the reference site.

It defaults to annotation.HomeOwnNode deliberately: a position added later inherits the lossless behaviour, and only a caller that can prove it already carries the annotations opts out through CarriedRef.

func ResidueKeywords

func ResidueKeywords() []string

ResidueKeywords returns that list, for the carrier lowerings outside this package that preserve the same set at their own positions.

It hands back a copy rather than the slice. Being one list is the whole point — a keyword added here has to reach every position that preserves one — and an exported slice is a mutable global: any importer could rewrite what every schema position in the process preserves, silently and for good.

func StampConstraintDiags

func StampConstraintDiags(c lowering.Ctx, diags []ir.Diagnostic, pointer string) []ir.Diagnostic

StampConstraintDiags gives every constraint diagnostic the provenance of the pointer that read the schema, which is what makes two reads of one sub-schema — its owning property and a $ref that hoists it — identical and so deduped by Diags.Append rather than reported twice.

Types

type AnchorIndex

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

AnchorIndex memoizes the document's $dynamicAnchor index.

It is the one thing this compiler shares and mutates besides the interning table, which micro-compiler-design §4 did not expect: it holds that interning is "irreducibly shared and stateful; nothing else is". This is the exception, and it is a memo rather than an accumulator — the value it caches is a pure function of the document.

It stays a memo rather than moving into the immutable context, which §4.1 prescribes, for two measured reasons recorded there: building it emits a diagnostic, so deriving it at entry reports on documents that never write $dynamicRef; and the walk costs about 1.4% of a compile, which is a poor trade for a keyword almost no document uses.

Jump to

Keyboard shortcuts

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