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
- func AppendExample(c lowering.Ctx, out []ir.Example, proto ir.Example, node *yaml.Node, ...) ([]ir.Example, []ir.Diagnostic)
- func CarriedRef(c lowering.Ctx, ts *compile.Types, anchors *AnchorIndex, depth int, ...) (ir.TypeRef, []ir.Diagnostic)
- func ExtensionsIn(c lowering.Ctx, ext *extensions.Extensions, owner, scope string) (ir.Unmodeled, []ir.Diagnostic)
- func ExtensionsOf(c lowering.Ctx, ext *extensions.Extensions, owner string) (ir.Unmodeled, []ir.Diagnostic)
- func FillPropertyDetail(c lowering.Ctx, ts *compile.Types, anchors *AnchorIndex, p *ir.Property, ...) []ir.Diagnostic
- func LowerComponentSchemas(ctx context.Context, c lowering.Ctx, ts *compile.Types, anchors *AnchorIndex) []ir.Diagnostic
- func LoweredToOwnNode(ts *compile.Types, pointer string, t ir.TypeRef) bool
- func Preserve(c lowering.Ctx, p *ir.Unmodeled, key string, raw ir.RawValue, ...)
- func PreserveNode(c lowering.Ctx, p *ir.Unmodeled, key string, node *yaml.Node, ...) (bool, []ir.Diagnostic)
- func PreserveRefSiteKeywords(c lowering.Ctx, ts *compile.Types, p *ir.Unmodeled, ...) []ir.Diagnostic
- func PreserveSchemaKeyword(c lowering.Ctx, p *ir.Unmodeled, s *oas3.Schema, keyword string, ...) (bool, []ir.Diagnostic)
- func PreserveUnknownKeywords(c lowering.Ctx, p *ir.Unmodeled, s *oas3.Schema, pointer string) []ir.Diagnostic
- func Ref(c lowering.Ctx, ts *compile.Types, anchors *AnchorIndex, depth int, ...) (ir.TypeRef, []ir.Diagnostic)
- func ResidueKeywords() []string
- func StampConstraintDiags(c lowering.Ctx, diags []ir.Diagnostic, pointer string) []ir.Diagnostic
- type AnchorIndex
Constants ¶
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 ¶
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.