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.
Cross-route conflict detection: reject route patterns that net/http's ServeMux refuses to register together, at gen time instead of at server boot.
Transport: path/query/header/cookie/form/response binding collection.
Transport: @default literal rendering + Go pre-fill code generation.
Wire-bind rendering: the per-source (query/header/cookie/path/form) binding descriptors, the primitive parse table, and the Go-source generator for one field's bind line.
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 ¶
- func GenerateEnums(pkg *semantic.Package, outDir string) error
- func GenerateErrors(pkg *semantic.Package, outDir string) error
- func GenerateErrorsPackage(pkg *semantic.Package, outDir string, r *ProjectResolver) error
- func GenerateOpenAPI(pkg *semantic.Package, cfg *config.Config, projectRoot string) error
- func GeneratePerServiceRoutes(pkg *semantic.Package, cfg *config.Config, projectRoot string) error
- func GenerateProjectMain(proj *semantic.Project, cfg *config.Config, projectRoot string) error
- func GenerateProjectMiddlewares(proj *semantic.Project, cfg *config.Config, projectRoot string) error
- func GenerateProjectOpenAPI(proj *semantic.Project, cfg *config.Config, projectRoot string) error
- func GenerateProjectRoutesUmbrella(proj *semantic.Project, cfg *config.Config, projectRoot string) error
- func GenerateRoutes(pkg *semantic.Package, cfg *config.Config, projectRoot string) error
- func GenerateRuntimeConfig(cfg *config.Config, projectRoot string) error
- func GenerateService(pkg *semantic.Package, cfg *config.Config, projectRoot string) error
- func GenerateServicePackage(pkg *semantic.Package, cfg *config.Config, projectRoot string, ...) error
- func GenerateSvccontext(cfg *config.Config, projectRoot string) error
- func GenerateTransport(pkg *semantic.Package, cfg *config.Config, projectRoot string) error
- func GenerateTransportResolved(pkg *semantic.Package, cfg *config.Config, projectRoot string, ...) error
- func GenerateTransportWith(pkg *semantic.Package, cfg *config.Config, projectRoot string, ...) error
- func GenerateTypes(pkg *semantic.Package, outDir string) error
- func GenerateTypesPackage(pkg *semantic.Package, outDir string, crossPkg CrossPkg, r *ProjectResolver) error
- func GenerateValidators(pkg *semantic.Package, outDir string) error
- func GenerateValidatorsAll(pkg *semantic.Package, outDir string, crossPkg CrossPkg, scalars ScalarTable, ...) error
- func GenerateValidatorsPackage(pkg *semantic.Package, outDir string, crossPkg CrossPkg) error
- func GenerateValidatorsResolved(pkg *semantic.Package, outDir string, r *ProjectResolver) error
- func GenerateValidatorsWith(pkg *semantic.Package, outDir string, crossPkg CrossPkg, scalars ScalarTable, ...) error
- func GoFieldName(name string) string
- func GoTypeRef(t *ast.TypeRef) string
- func ServiceDir(svcName string) string
- func ServicePackage(svcName string) string
- func ValidateProjectOpenAPI(proj *semantic.Project, cfg *config.Config) error
- func ValidateRouteConflicts(proj *semantic.Project, cfg *config.Config) []string
- func ValidateSecurityRefs(pkg *semantic.Package, cfg *config.Config) []string
- type Binding
- type CrossPkg
- type EnumTable
- type ErrorTable
- type MiddlewareTable
- type ProjectResolver
- func (r *ProjectResolver) ImportPath(pkgAlias string) string
- func (r *ProjectResolver) LookupEnum(name string) *ast.EnumDecl
- func (r *ProjectResolver) LookupError(name string) *ast.ErrorDecl
- func (r *ProjectResolver) LookupMiddleware(name string) *ast.MiddlewareDecl
- func (r *ProjectResolver) LookupScalar(name string) *ast.ScalarDecl
- func (r *ProjectResolver) LookupType(name string) *ast.TypeDecl
- func (r *ProjectResolver) QualifierFor(n *ast.NamedTypeRef) (string, string)
- type ResolvedField
- type ScalarTable
- type TypeTable
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func GenerateEnums ¶
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 ¶
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 ¶
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 ¶
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 ¶
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:
- The IMPLEMENTATION at `<output.middleware>/<kebab-name>-middleware.go`. Scaffold-only - gen-once; existing files are left alone.
- 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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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
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 ValidateRouteConflicts ¶ added in v1.3.8
ValidateRouteConflicts reports every pair of routes across the whole project that net/http's ServeMux (Go 1.22+) would reject at registration because they overlap and neither is more specific than the other - e.g. `GET /orders/{id}/track` vs `GET /orders/by-status/{status}`, which both match `/orders/by-status/track`. ServeMux panics on these at server boot; surfacing them here turns a runtime crash into a design-time error. Returns one message per conflicting pair (empty when the route set is clean).
All services register on one mux (routes.RegisterAll), so the check spans every package; the OpenAPI basePath is a shared prefix and does not change which pairs conflict.
func ValidateSecurityRefs ¶
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).
type CrossPkg ¶
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 ¶
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 ¶
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.
type ErrorTable ¶
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 ¶
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.
Source Files
¶
- cross_pkg.go
- enums.go
- errors.go
- main.go
- middleware.go
- openapi.go
- openapi_fields.go
- openapi_generic.go
- openapi_merge.go
- openapi_operation.go
- openapi_paths.go
- openapi_schemas.go
- openapi_security.go
- openapi_typeref.go
- paths.go
- resolved.go
- resolver.go
- route_conflicts.go
- routes.go
- runtime.go
- service.go
- templates.go
- transport.go
- transport_bindings.go
- transport_defaults.go
- transport_wirebind.go
- types.go
- validate.go
- validate_args.go
- validate_emit.go
- validate_emit_array.go
- validate_emit_format.go
- validate_emit_nested.go
- validate_emit_numeric.go
- validate_emit_required.go
- validate_emit_scalar.go
- validate_emit_string.go
- validate_registry.go
- validate_types.go