/// Gleam Code IR - intermediate representation for generated Gleam source files. /// This separates schema semantics from text rendering, preventing the /// duplication of logic across types/decoders/client generators. import gleam/option.{type Option} /// A complete generated Gleam source file. /// /// Opaque: construct via `module/3`, read via `module_header/1`, /// `module_imports/1`, and `module_declarations/1`. The internal shape /// can then evolve (e.g. to track file-level doc comments) without /// rippling into every renderer pass. pub opaque type Module { Module( header: String, imports: List(String), declarations: List(Declaration), header_records: List(ResponseHeaderRecord), ) } /// Construct a `Module` from its three component pieces. pub fn module( header header: String, imports imports: List(String), declarations declarations: List(Declaration), ) -> Module { Module(header:, imports:, declarations:, header_records: []) } /// Construct a `Module` with header records. pub fn module_with_header_records( header header: String, imports imports: List(String), declarations declarations: List(Declaration), header_records header_records: List(ResponseHeaderRecord), ) -> Module { Module(header:, imports:, declarations:, header_records:) } /// Module imports, in the order they should be rendered. pub fn module_imports(m: Module) -> List(String) { m.imports } /// Top-level declarations inside the module. pub fn module_declarations(m: Module) -> List(Declaration) { m.declarations } /// A top-level declaration with optional doc comment. /// /// Opaque: construct via `declaration/2`, read via `declaration_doc/1` /// and `declaration_type_def/1`. pub opaque type Declaration { Declaration(doc: Option(String), type_def: TypeDef) } /// Construct a `Declaration` from its optional doc and type definition. pub fn declaration( doc doc: Option(String), type_def type_def: TypeDef, ) -> Declaration { Declaration(doc:, type_def:) } /// Optional doc comment attached to this declaration. pub fn declaration_doc(d: Declaration) -> Option(String) { d.doc } /// The type definition carried by this declaration. pub fn declaration_type_def(d: Declaration) -> TypeDef { d.type_def } /// The shapes of type definition that the OpenAPI generator produces. pub type TypeDef { /// `pub type Foo = Bar` TypeAlias(name: String, target: String) /// `pub type Foo { Foo(field1: Type1, field2: Type2) }` RecordType(name: String, fields: List(Field)) /// `pub type Foo { FooBar(Bar) FooBaz(Baz) }` UnionType(name: String, variants: List(Variant)) /// `pub type Status { StatusActive StatusInactive }` EnumType(name: String, variants: List(String)) } /// A named, typed field in a record constructor. pub type Field { Field(name: String, type_expr: String) } /// A variant in a union type. pub type Variant { /// Variant with a wrapped type: `FooBar(Bar)` VariantWithType(name: String, inner_type: String) /// Variant with no payload: `FooNone` VariantEmpty(name: String) /// Response variant carrying a body and a typed headers record: /// `FooBar(Bar, FooBarHeaders)` (issue #306). VariantWithTypeAndHeaders( name: String, inner_type: String, headers_type: String, ) /// Response variant carrying only a typed headers record (no body): /// `FooBar(FooBarHeaders)` (issue #306). VariantWithHeaders(name: String, headers_type: String) /// Response variant for the OpenAPI `default` catch-all. Always /// carries an Int status code as the first positional field, so /// handlers can pick any 4xx/5xx for the catch-all branch instead /// of the router pinning every response to 500. The body and /// headers types are optional, mirroring the four shapes of the /// other variant kinds (issue #483): /// /// * neither → `FooDefault(Int)` /// * body only → `FooDefault(Int, Body)` /// * headers only → `FooDefault(Int, FooDefaultHeaders)` /// * body + headers → `FooDefault(Int, Body, FooDefaultHeaders)` VariantDefault( name: String, inner_type: Option(String), headers_type: Option(String), ) } /// A typed record for response headers. pub type ResponseHeaderRecord { ResponseHeaderRecord(name: String, fields: List(Field)) } /// Response header records inside the module. pub fn module_header_records(m: Module) -> List(ResponseHeaderRecord) { m.header_records }