codegen

package
v1.3.6 Latest Latest
Warning

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

Go to latest
Published: Jun 6, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

OpenAPI codegen uses `github.com/getkin/kin-openapi` to build a strongly- typed openapi3.T document and `sigs.k8s.io/yaml` to render it as YAML. The library handles spec compliance, default ordering, and the small quirks of OpenAPI 3.1; we only translate craftgo's AST into its types.

Field-level decorator -> schema metadata mapping.

Multi-package merge + clone/rewrite helpers for the OpenAPI document builder.

Operation assembly: buildOperation, parameters, errors, tags, security.

Path + per-operation request/response schemas + response headers.

Top-level component schemas: types, enums, scalars, errors.

OpenAPI security scheme components emission + manifest cross-check.

TypeRef -> SchemaRef conversion + generic instantiation.

Resolved field/type IR: the single, fully-resolved view of a type's fields that every codegen stage consumes — so no stage re-walks the AST and re-derives a field fact (is-on-wire, is-required, is-pointer, wire name, default value) differently from another and drifts.

This is the anti-drift layer the cross-stage audits kept pointing at: the same fact was computed in three-plus places (fieldIsRequired vs the validator's presence gate vs the wire-presence gate; isNonBodyBound vs the schema header/cookie skip; flattenFields vs a stage's own td.Body walk). [resolveFields] computes each fact ONCE, from the same helpers the stages used to call inline, so a consumer that reads a ResolvedField gets a value that cannot disagree with another consumer's.

Transport: path/query/header/cookie/form/response binding collection.

Transport: @default literal rendering + Go pre-fill code generation.

Package codegen turns a validated semantic.Package into Go source files.

Each generator function in this package is responsible for one artefact (currently only types.go; handler/routes/logic scaffolds and OpenAPI/client generators land in subsequent slices). Output files are formatted with `go/format` before being written so that diffs in version control are minimal and the generated code passes `gofmt`.

Array / file validators: @minItems/@maxItems/@uniqueItems/@maxSize/@mimeTypes.

Format-validator catalogue: per-@format(name) check builders + RFC references.

Nested / enum / type-param validator dispatch.

Numeric validators: @gt/@gte/@lt/@lte/@range/@positive/@negative/@multipleOf.

Required-presence validators emitted onto the generated Validate() chain.

String validators: @length, @minLength, @maxLength, @pattern, @format dispatcher.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateEnums

func GenerateEnums(pkg *semantic.Package, outDir string) error

GenerateEnums writes enums.go under outDir/<pkg.Name>/ with a Go type alias and const block per enum. No-op when pkg has no enums.

The Go base type is `int` for int-valued enums, `string` otherwise. Constants are named `<EnumName><PascalCase(ValueName)>`; bare values use the value name as the string payload.

func GenerateErrors

func GenerateErrors(pkg *semantic.Package, outDir string) error

GenerateErrors emits a single `errors.go` file under outDir/<pkg.Name>/ declaring one struct + constructor + Error()/HTTPStatus() methods + SCREAMING_SNAKE error-code constant for every ast.ErrorDecl in pkg. When pkg has no errors the function is a no-op.

Equivalent to GenerateErrorsPackage with a nil resolver; kept for single-package callers that don't reach across packages.

func GenerateErrorsPackage

func GenerateErrorsPackage(pkg *semantic.Package, outDir string, r *ProjectResolver) error

GenerateErrorsPackage is the multi-package variant of GenerateErrors. The ProjectResolver supplies the cross-package import paths for body fields (e.g. an error in `tasks` whose body carries a `users.UserRef`) AND the cross-package scalar / enum resolution needed to format a non-string `@header` / `@cookie` error field (`cost shared.Cents`). A nil resolver falls back to local-only resolution.

func GenerateOpenAPI

func GenerateOpenAPI(pkg *semantic.Package, cfg *config.Config, projectRoot string) error

GenerateOpenAPI builds an OpenAPI 3.1 document for pkg and writes it as YAML to the path configured by `output.openapi`. Each service contributes one set of operations under its `@prefix`; every concrete TypeDecl becomes a schema in `components.schemas`.

func GeneratePerServiceRoutes

func GeneratePerServiceRoutes(pkg *semantic.Package, cfg *config.Config, projectRoot string) error

GeneratePerServiceRoutes emits only the per-service `routes.go` files; the umbrella is left to a project-level pass. Used by the multi-package CLI flow so each package's services contribute to a single shared umbrella rather than overwriting each other.

func GenerateProjectMain

func GenerateProjectMain(proj *semantic.Project, cfg *config.Config, projectRoot string) error

GenerateProjectMain scaffolds the project's main.go (`output.main`) using the union of services and middlewares from every package. The single shared umbrella `routes.RegisterAll` (emitted by GenerateProjectRoutesUmbrella) is the one-call wire-up so the template doesn't have to import per-package routes packages.

The file is gen-once: written when missing, skipped on subsequent gen runs so user-written boot code (extra middlewares, config loading, OTel SDK setup, etc.) survives regeneration.

Setting `output.main: "-"` in the manifest opts the project out of scaffolding entirely - useful for test fixtures that ship their own httptest server and would collide with a generated `package main`.

func GenerateProjectMiddlewares

func GenerateProjectMiddlewares(proj *semantic.Project, cfg *config.Config, projectRoot string) error

GenerateProjectMiddlewares emits the unified `svccontext/middlewares.go` + per-middleware scaffolds for every package in the project. Middleware names are global (the project resolver enforces uniqueness), so a single Middlewares struct embeds every declaration regardless of which package it lives in. Run ONCE per `craftgo gen` instead of per-package.

Two artefacts per `middleware Name` block:

  1. The IMPLEMENTATION at `<output.middleware>/<kebab-name>-middleware.go`. Scaffold-only - gen-once; existing files are left alone.
  2. The TYPE declaration list at `<svccontext-dir>/middlewares.go`. Always overwritten - derived purely from the DSL.

Users embed the generated `Middlewares` struct into their own ServiceContext, then assign each field to a concrete impl in main.go. Routes consume the middleware via the embedded fields directly, so no runtime name registry lookup is needed.

func GenerateProjectOpenAPI

func GenerateProjectOpenAPI(proj *semantic.Project, cfg *config.Config, projectRoot string) error

GenerateProjectOpenAPI is the multi-package counterpart of GenerateOpenAPI: it merges every package's types/enums/errors/ scalars/services into a single OpenAPI 3.1 document. When two packages declare a same-named entity, the second-and-subsequent occurrences get renamed to `<PascalPkg><Name>` (e.g. two packages each declaring `User` produce `User` for the first-seen and `AuthUser` / `SharedUser` for collisions). Non-conflicting names stay bare so simple projects keep readable schema names.

func GenerateProjectRoutesUmbrella

func GenerateProjectRoutesUmbrella(proj *semantic.Project, cfg *config.Config, projectRoot string) error

GenerateProjectRoutesUmbrella emits the top-level `<output.routes>/routes.go` that exposes `RegisterAll(srv, svcCtx)`, aggregating every service from every DSL package in the project. When no package declares a service the file is skipped - calling `RegisterAll` from main.go would also be a no-op.

func GenerateRoutes

func GenerateRoutes(pkg *semantic.Package, cfg *config.Config, projectRoot string) error

GenerateRoutes emits one `routes.go` per service under `<output.routes>/<servicePackage>/` PLUS a top-level `<output.routes>/routes.go` that exposes `RegisterAll(srv, svcCtx)` - the one-call wire-up consumed by main.go. Both layers are regenerated on every gen because they're derived purely from the DSL service set.

Single-package callers should keep using this entry point. Multi- package projects call GeneratePerServiceRoutes per package and GenerateProjectRoutesUmbrella once for the project so the umbrella aggregates services from every package.

func GenerateRuntimeConfig

func GenerateRuntimeConfig(cfg *config.Config, projectRoot string) error

GenerateRuntimeConfig scaffolds the project's `config/` package (`config.go` + `config.yaml` + `example.config.yaml`) under `cfg.Output.Config`. Every file is gen-once: written when missing, left untouched when present. main.go reads `<Config>/config.yaml` at boot and hands the loaded `*config.Config` to `svccontext.NewServiceContext`.

Skipped when `cfg.Output.Main == "-"` - projects opting out of the generated main.go (test fixtures, library-style modules) don't need the runtime config package; emitting it would only add a stray import and force the module to track yaml.v3.

The template body lives in `internal/codegen/templates/`. Edit those files to change the shape of the scaffolded artefact - per-project overrides are out of scope here (the runtime config is meant to be edited freely after the first gen).

func GenerateService

func GenerateService(pkg *semantic.Package, cfg *config.Config, projectRoot string) error

GenerateService scaffolds one `<method>.go` per method per service under `<output.service>/<servicePackage>/`. Unlike the other generators this one runs in **scaffold** mode: existing files are left untouched so user-written business logic is never overwritten.

Equivalent to GenerateServicePackage with a nil CrossPkg context.

func GenerateServicePackage

func GenerateServicePackage(pkg *semantic.Package, cfg *config.Config, projectRoot string, crossPkg CrossPkg) error

GenerateServicePackage is the multi-package variant of GenerateService. crossPkg lets the scaffold render `*foo.Cred` for a cross-package request/response type rather than the legacy `*types.Cred`.

func GenerateSvccontext

func GenerateSvccontext(cfg *config.Config, projectRoot string) error

GenerateSvccontext scaffolds `svccontext.go` at the location pointed to by `cfg.Output.Svccontext`. The file accepts a `*config.Config` in its constructor and embeds the codegen-managed `Middlewares` struct (which is regenerated next to it on every gen run).

Gen-once: existing svccontext.go is left untouched so user-added fields (database handles, caches, ...) survive regeneration. The adjacent `middlewares.go` IS regenerated; splitting the two keeps the auto-managed struct from colliding with hand-edited code.

Skipped when `cfg.Output.Main == "-"` - same rationale as GenerateRuntimeConfig: opting out of main.go means the project doesn't want the framework's runtime scaffolding in its module.

func GenerateTransport

func GenerateTransport(pkg *semantic.Package, cfg *config.Config, projectRoot string) error

GenerateTransport emits one `<method>_handler.go` per method per service under `<output.handler>/<servicePackage>/`. Each file contains a single exported `<Method>Handler(svcCtx) http.HandlerFunc` constructor that decodes the request, calls the user's logic, and writes the response.

projectRoot is prepended to `cfg.Output.Transport` so the function can be called with paths relative to the manifest's directory.

Equivalent to GenerateTransportWith with nil CrossPkg and nil ScalarTable - the convenience entry single-package tests reach for. Production CLI flows go straight through GenerateTransportWith because they always have a project-wide cross-package table to feed in.

func GenerateTransportResolved

func GenerateTransportResolved(pkg *semantic.Package, cfg *config.Config, projectRoot string, r *ProjectResolver) error

GenerateTransportResolved is the canonical entry point. The ProjectResolver supplies every project-wide lookup the handler emit chain may consult — scalar inheritance, cross-package enum/type resolution for binding casts, and the Go import paths the generated handler file needs when it emits qualified identifiers. nil resolver yields the legacy single-package behaviour: only `pkg`'s local symbols resolve.

func GenerateTransportWith

func GenerateTransportWith(pkg *semantic.Package, cfg *config.Config, projectRoot string, crossPkg CrossPkg, scalars ScalarTable) error

GenerateTransportWith is the explicit-tables entry for single-package tests that build CrossPkg / ScalarTable directly. GenerateTransportResolved accepts a ProjectResolver bundling every cross-package table.

func GenerateTypes

func GenerateTypes(pkg *semantic.Package, outDir string) error

GenerateTypes emits a `types.go` file under outDir/<pkg.Name>/ containing Go struct definitions for every concrete (non-generic) ast.TypeDecl in pkg. Generic declarations (those with ast.TypeDecl.TypeParams) are skipped - their concrete instances are emitted at the call site once generic instantiation lands.

outDir is the configured `output.types` directory; the package name segment is appended so that types live alongside the rest of the service-scoped artefacts.

Equivalent to GenerateTypesPackage with a nil CrossPkg context; the legacy single-package signature stays so existing callers / tests keep working unchanged.

func GenerateTypesPackage

func GenerateTypesPackage(pkg *semantic.Package, outDir string, crossPkg CrossPkg, r *ProjectResolver) error

GenerateTypesPackage is the multi-package variant of GenerateTypes. crossPkg adds Go imports for every cross-package DSL alias used in pkg's field types or mixin refs - when nil/empty the output is identical to single-package codegen.

func GenerateValidators

func GenerateValidators(pkg *semantic.Package, outDir string) error

GenerateValidators writes `validate.go` next to `types.go`. The file adds a `Validate() error` method to every concrete TypeDecl. Types without any constraints get an empty stub so handlers can call `req.Validate()` uniformly.

Equivalent to GenerateValidatorsPackage with a nil CrossPkg context, for single-package callers and tests.

func GenerateValidatorsAll

func GenerateValidatorsAll(pkg *semantic.Package, outDir string, crossPkg CrossPkg, scalars ScalarTable, types TypeTable, enums EnumTable) error

GenerateValidatorsAll is the explicit-tables entry point for tests that build tables directly; GenerateValidatorsResolved accepts a single ProjectResolver instead of four ad-hoc tables. This wrapper assembles a resolver from the parameters and delegates.

func GenerateValidatorsPackage

func GenerateValidatorsPackage(pkg *semantic.Package, outDir string, crossPkg CrossPkg) error

GenerateValidatorsPackage is the multi-package variant of GenerateValidators. crossPkg adds Go imports for every cross- package alias used in pkg's field types so `req.User.Validate()` can dispatch to the sibling package's validator.

Equivalent to GenerateValidatorsWith with a nil scalar table: scalar inheritance is disabled in this entry point.

func GenerateValidatorsResolved

func GenerateValidatorsResolved(pkg *semantic.Package, outDir string, r *ProjectResolver) error

GenerateValidatorsResolved is the canonical entry point. It takes a single ProjectResolver carrying every cross-package lookup the validator emit chain needs — scalar inheritance, generic Validate dispatch, cross-pkg enum value-set checks, and the matching Go import registrations. nil resolver is tolerated and degrades to local-only behaviour, matching the legacy single-package shape.

func GenerateValidatorsWith

func GenerateValidatorsWith(pkg *semantic.Package, outDir string, crossPkg CrossPkg, scalars ScalarTable, types TypeTable) error

GenerateValidatorsWith is the project-aware entry point: it accepts the ScalarTable built by BuildScalarTable so a field typed `Email` (local scalar) or `shared.NonEmptyID` (cross-pkg scalar) inherits the scalar's own decorator chain into its generated Validate() body. The TypeTable resolves qualified type refs (`shared.Page<T>`), which the local-only `pkg.Types` lookup cannot reach, so they emit recursive `.Validate()` calls.

Used by the multi-package CLI flow; single-package fixtures and tests continue calling GenerateValidators / GenerateValidatorsPackage which pass nil for the tables.

func GoFieldName

func GoFieldName(name string) string

GoFieldName converts a DSL field name (which is allowed to be lowercase, snake_case, or camelCase) into an exported Go identifier applying the common-initialism rule. The DSL field name is preserved verbatim as the JSON tag - see [jsonTag]. Implementation lives in internal/idents so the semantic analyser can detect collisions using the same conversion rule that codegen emits.

func GoTypeRef

func GoTypeRef(t *ast.TypeRef) string

GoTypeRef converts an ast.TypeRef into the corresponding Go type expression. The optional suffix (`?`) prepends `*` only when the underlying Go type isn't already nilable: slices, maps, channels, interfaces, and pointer-shaped builtins (`file` → `*multipart.FileHeader`) already use `nil` as their zero value, so wrapping them in another pointer would just produce `**T` / `*[]T` for no semantic gain. Value types (`string`, `int`, user structs) still receive `*` so the generated field can distinguish "absent" from the zero value.

func ServiceDir

func ServiceDir(svcName string) string

ServiceDir returns the kebab-case directory name for a service. Used for filesystem paths and import segments - `UserService` becomes `user-service`. The Go package declaration inside the directory still uses ServicePackage (no hyphens) so the source remains compilable.

func ServicePackage

func ServicePackage(svcName string) string

ServicePackage returns the Go-identifier package name for a service. Service names use PascalCase in the DSL ("UserService"); the matching Go package declaration is the lowercase concatenation ("userservice") because Go identifiers cannot contain hyphens.

func ValidateProjectOpenAPI added in v1.1.0

func ValidateProjectOpenAPI(proj *semantic.Project, cfg *config.Config) error

ValidateProjectOpenAPI runs every OpenAPI-level uniqueness check (operationId + component-schema names) against the merged project WITHOUT writing anything. The CLI calls it as a pre-flight before any codegen step touches disk, so a name collision fails the whole run up front instead of after types/transport are already written.

func ValidateSecurityRefs

func ValidateSecurityRefs(pkg *semantic.Package, cfg *config.Config) []string

ValidateSecurityRefs cross-checks every `@security(scheme)` reference in pkg against the manifest's declared `openapi.securitySchemes` map. The check is permissive when the manifest declares no schemes: in that case we keep the legacy auto-generated bearer behaviour (so projects that haven't migrated continue to work). When the manifest HAS declared at least one scheme, every reference must resolve to a key in that map; unknown references produce a sorted list of error strings the caller can format. To express "this endpoint is public" use `@ignoreSecurity` at the method level rather than a sentinel scheme name.

Types

type Binding added in v1.2.0

type Binding int

Binding is where a field's value rides on the wire, derived from its explicit binding decorator (auto-@path / auto-@query are method-context dependent and applied by the request-binding stage, not here).

const (
	BindBody Binding = iota // JSON request/response body (the default)
	BindPath
	BindQuery
	BindHeader
	BindCookie
	BindForm
	BindSensitive // @sensitive: server-only, json:"-", excluded everywhere
)

func (Binding) String added in v1.2.0

func (b Binding) String() string

String renders the OpenAPI `in` value for the wire bindings; body and sensitive have no `in`.

type CrossPkg

type CrossPkg map[string]string

CrossPkg maps a DSL package name (the target's `package X` declaration) to the Go import path under `<modulePath>/<typesOutputDir>/<X>`. Generators look up multi-part DSL refs (`shared.User`) by their first segment to decide which Go import statement to add.

A nil or empty CrossPkg indicates no cross-package context - the generators emit only the standard-library imports they have always emitted.

func BuildCrossPkg

func BuildCrossPkg(proj *semantic.Project, cfg *config.Config, currentPkgName string) CrossPkg

BuildCrossPkg returns a fully-populated lookup table for every non-current package in the project. The current package is excluded so a self-reference (`design.Foo` inside `package design`) renders as bare `Foo` without dragging in a redundant Go import.

Caller passes `currentPkgName` = the package being generated; passing "" returns the entire project mapping (useful for tools that don't know the destination yet).

type EnumTable

type EnumTable map[string]*ast.EnumDecl

EnumTable is the per-target-package lookup of EnumDecls reachable from the package being generated. Local enums are keyed bare (`Color`); cross-package enums by qualified DSL form (`shared.Color`). The validator codegen consults this so a field typed `color shared.Color` emits the switch-case validity check for a qualified ref the local-only `pkg.Enums` lookup misses.

func BuildEnumTable

func BuildEnumTable(proj *semantic.Project, currentPkgName string) EnumTable

BuildEnumTable returns the lookup table for `currentPkgName`. Every enum declared anywhere in the project is included once.

type ErrorTable

type ErrorTable map[string]*ast.ErrorDecl

ErrorTable is the per-target-package lookup of ErrorDecls reachable from the package being generated. Mirrors TypeTable: local errors keyed bare (`NotFoundErr`), cross-package errors by qualified DSL form (`shared.NotFoundErr`). The OpenAPI emitter needs this to resolve `@errors(shared.NotFoundErr)` references against the right per-error response schema and to register the error body component.

func BuildErrorTable

func BuildErrorTable(proj *semantic.Project, currentPkgName string) ErrorTable

BuildErrorTable returns the lookup table for `currentPkgName`. Mirrors BuildTypeTable / BuildEnumTable.

type MiddlewareTable

type MiddlewareTable map[string]*ast.MiddlewareDecl

MiddlewareTable is the per-target-package lookup of MiddlewareDecls reachable from the package being generated. Same keying contract as the other tables, included for symmetry with the codegen-side lookups so a cross-pkg middleware rule has the same plumbed lookup as everything else.

func BuildMiddlewareTable

func BuildMiddlewareTable(proj *semantic.Project, currentPkgName string) MiddlewareTable

BuildMiddlewareTable returns the lookup table for `currentPkgName`.

type ProjectResolver

type ProjectResolver struct {
	Types       TypeTable
	Enums       EnumTable
	Scalars     ScalarTable
	Errors      ErrorTable
	Middlewares MiddlewareTable
	CrossPkg    CrossPkg
}

ProjectResolver bundles every per-package-target lookup table the codegen layer needs to resolve qualified cross-package references. One resolver per generated package; built by BuildProjectResolver from a semantic.Project + config.Config.

Pass it as a single parameter instead of plumbing 4-5 separate tables. Lookup methods are nil-tolerant — `(*ProjectResolver)(nil)` is a usable zero value that always misses, matching the legacy behaviour every callsite already handles for `nil` maps.

func BuildProjectResolver

func BuildProjectResolver(proj *semantic.Project, cfg *config.Config, currentPkgName string) *ProjectResolver

BuildProjectResolver assembles every table the codegen layer needs for `currentPkgName`. Returns a non-nil resolver with empty tables when proj is nil so callers don't have to nil-check before use.

func (*ProjectResolver) ImportPath

func (r *ProjectResolver) ImportPath(pkgAlias string) string

ImportPath returns the Go import path for the DSL package alias, or "" when the alias isn't in the cross-package map. Used by emit sites that need to register an import when they output a qualified Go identifier like `shared.ColorRed`.

func (*ProjectResolver) LookupEnum

func (r *ProjectResolver) LookupEnum(name string) *ast.EnumDecl

LookupEnum is the enum counterpart of [LookupType].

func (*ProjectResolver) LookupError

func (r *ProjectResolver) LookupError(name string) *ast.ErrorDecl

LookupError is the error counterpart of [LookupType].

func (*ProjectResolver) LookupMiddleware

func (r *ProjectResolver) LookupMiddleware(name string) *ast.MiddlewareDecl

LookupMiddleware is the middleware counterpart of [LookupType].

func (*ProjectResolver) LookupScalar

func (r *ProjectResolver) LookupScalar(name string) *ast.ScalarDecl

LookupScalar is the scalar counterpart of [LookupType].

func (*ProjectResolver) LookupType

func (r *ProjectResolver) LookupType(name string) *ast.TypeDecl

LookupType returns the type decl bound to name (bare for local, qualified `pkg.Name` for cross-package), or nil when no match.

func (*ProjectResolver) QualifierFor

func (r *ProjectResolver) QualifierFor(n *ast.NamedTypeRef) (string, string)

QualifierFor inspects a named ref and returns (goPrefix, importPath):

  • goPrefix is the Go package qualifier WITH trailing dot (e.g. `"shared."`) for cross-pkg refs, empty for local
  • importPath is the Go import path to register on the generated file when the prefix is non-empty

Returns ("", "") for nil receiver / nil ref / bare ref so callers can use the result unconditionally:

prefix, path := r.QualifierFor(n)
if path != "" { uses[path] = true }
emit(prefix + ed.Name + ...)

type ResolvedField added in v1.2.0

type ResolvedField struct {
	// Field is the (generic-substituted) source field; stages that still
	// need raw decorators or the type ref read it from here.
	Field *ast.Field

	DSLName string // wire/json base name (the source identifier)
	GoName  string // exported Go field identifier
	GoType  string // final Go type, including any *T nullable wrap
	JSONTag string // struct json tag, including ,omitempty / "-"

	Binding    Binding // wire placement (after request auto-binding, if any)
	OnWireBody bool    // appears as a property in the JSON body schema/struct
	// AutoBound is true when resolveRequestFields promoted an un-decorated
	// field to @path / @query (vs an explicit binding). Stages use it to
	// distinguish an explicit binding that fails to lower (a hard error)
	// from an auto-promoted field that merely can't ride the wire (skipped
	// silently). Always false for response/explicit fields.
	AutoBound bool

	IsPointer     bool // generated Go type is *T
	NeedsNilGuard bool // a constraint check must nil-guard before len()/deref

	HasDefault  bool // carries @default
	DefaultWire any  // resolved OpenAPI default value (enum member -> wire), nil if none
	HasDefValue bool // a default value was resolved

	// SpecRequired: the field belongs in the OpenAPI required[] (not
	// optional, no @default). RuntimeEnforced: the validator emits a
	// presence check for it (not optional, not @nullable — pointer-backed
	// fields are presence-checked via nil). Stored side-by-side so the
	// schema/param/validate stages read ONE answer each, and a test can
	// assert their relationship as a single visible invariant rather than
	// an emergent property of separate predicates. They differ by design on
	// @default (excluded from SpecRequired) and @nullable (excluded from
	// RuntimeEnforced) — the test pins exactly where.
	SpecRequired    bool
	RuntimeEnforced bool
}

ResolvedField is the resolved view of one field after mixin flattening and generic-argument substitution. Every value is computed from the canonical helper, so the field is the single source of truth a stage reads instead of recomputing.

func (ResolvedField) WireName added in v1.2.0

func (rf ResolvedField) WireName() string

WireName returns the field's wire parameter name for its explicit binding (the decorator's name arg, or the field name). Empty for a body or sensitive field.

type ScalarTable

type ScalarTable map[string]*ast.ScalarDecl

ScalarTable is the per-target-package lookup of scalar declarations reachable from the package being generated. Local scalars are keyed by bare name (`OrderID`); cross-package scalars use the qualified DSL form (`shared.NonEmptyID`). The codegen consults the table when a field's declared type is a scalar so the scalar's decorators (e.g. `@format(email)` on `scalar Email`) inherit into the field's effective validator chain.

Empty / nil table disables inheritance and the generated validators only honour the field's own decorator list - the legacy single-package behaviour.

func BuildScalarTable

func BuildScalarTable(proj *semantic.Project, currentPkgName string) ScalarTable

BuildScalarTable returns the lookup table for `currentPkgName`. Every scalar declared anywhere in the project is included once; scalars from other packages are keyed by their qualified DSL form so a field typed `shared.NonEmptyID` resolves cleanly.

Returns nil when proj is nil - callers can still pass the result straight into GenerateValidatorsPackage without a guard.

type TypeTable

type TypeTable map[string]*ast.TypeDecl

TypeTable is the per-target-package lookup of TypeDecls reachable from the package being generated. Mirrors ScalarTable but for struct-shaped types: local types are keyed by bare name (`Order`), cross-package types by qualified form (`shared.Page`). The codegen consults the table to decide whether a field type carries its own `Validate()` method, so a qualified ref like `shared.Page<T>` (which the local-only `pkg.Types` lookup misses) still emits its recursive validate call.

func BuildTypeTable

func BuildTypeTable(proj *semantic.Project, currentPkgName string) TypeTable

BuildTypeTable returns the lookup table for `currentPkgName`. Every type declared anywhere in the project is included once, qualified for cross-package entries the same way scalars are.

Jump to

Keyboard shortcuts

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