Packages
oaspec
0.62.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.0
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.1
0.58.0
0.57.0
0.56.0
0.55.0
0.54.0
0.53.0
0.52.0
0.51.0
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.0
0.43.0
0.42.0
0.41.0
0.40.0
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.0
0.31.0
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.3
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.1.3
Generate Gleam code from OpenAPI 3.x specifications
Current section
Files
Jump to
Current section
Files
src/oaspec/internal/codegen/ir_render.gleam
/// Renderer for Gleam Code IR -> source text.
/// Pure function: takes IR, produces String. No IO.
import gleam/list
import gleam/option.{None, Some}
import gleam/string
import oaspec/internal/codegen/context
import oaspec/internal/codegen/ir.{
type Declaration, type Module, type TypeDef, EnumType, RecordType, TypeAlias,
UnionType, VariantDefault, VariantEmpty, VariantWithHeaders, VariantWithType,
VariantWithTypeAndHeaders,
}
import oaspec/internal/util/string_extra as se
/// Render a complete IR Module to a Gleam source string.
pub fn render(module: Module) -> String {
let sb =
se.file_header(context.version)
|> se.imports(ir.module_imports(module))
let sb =
list.fold(ir.module_declarations(module), sb, fn(sb, decl) {
render_declaration(sb, decl)
})
let sb =
list.fold(ir.module_header_records(module), sb, fn(sb, rec) {
render_header_record(sb, rec)
})
se.to_string(sb)
}
/// Render a single declaration (doc comment + type definition).
fn render_declaration(
sb: se.StringBuilder,
decl: Declaration,
) -> se.StringBuilder {
let sb = case ir.declaration_doc(decl) {
Some(doc) -> sb |> se.doc_comment(doc)
None -> sb
}
render_type_def(sb, ir.declaration_type_def(decl))
}
/// Render a type definition to Gleam source.
fn render_type_def(sb: se.StringBuilder, type_def: TypeDef) -> se.StringBuilder {
case type_def {
TypeAlias(name:, target:) ->
sb
|> se.line("pub type " <> name <> " = " <> target)
|> se.blank_line()
RecordType(name:, fields:) -> {
let sb = sb |> se.line("pub type " <> name <> " {")
let field_strs = list.map(fields, fn(f) { f.name <> ": " <> f.type_expr })
let sb =
sb
|> se.indent(1, name <> "(" <> string.join(field_strs, ", ") <> ")")
sb
|> se.line("}")
|> se.blank_line()
}
UnionType(name:, variants:) -> {
let sb = sb |> se.line("pub type " <> name <> " {")
let sb =
list.fold(variants, sb, fn(sb, variant) {
case variant {
VariantWithType(name: vname, inner_type:) ->
sb |> se.indent(1, vname <> "(" <> inner_type <> ")")
VariantEmpty(name: vname) -> sb |> se.indent(1, vname)
VariantWithTypeAndHeaders(name: vname, inner_type:, headers_type:) ->
sb
|> se.indent(
1,
vname <> "(" <> inner_type <> ", " <> headers_type <> ")",
)
VariantWithHeaders(name: vname, headers_type:) ->
sb |> se.indent(1, vname <> "(" <> headers_type <> ")")
VariantDefault(name: vname, inner_type:, headers_type:) -> {
let parts = case inner_type, headers_type {
None, None -> ["Int"]
Some(t), None -> ["Int", t]
None, Some(h) -> ["Int", h]
Some(t), Some(h) -> ["Int", t, h]
}
sb
|> se.indent(1, vname <> "(" <> string.join(parts, ", ") <> ")")
}
}
})
sb
|> se.line("}")
|> se.blank_line()
}
EnumType(name:, variants:) -> {
let sb = sb |> se.line("pub type " <> name <> " {")
let sb =
list.fold(variants, sb, fn(sb, variant_name) {
sb |> se.indent(1, variant_name)
})
sb
|> se.line("}")
|> se.blank_line()
}
}
}
/// Render a response header record type.
fn render_header_record(
sb: se.StringBuilder,
rec: ir.ResponseHeaderRecord,
) -> se.StringBuilder {
let name = rec.name
let field_strs = list.map(rec.fields, fn(f) { f.name <> ": " <> f.type_expr })
sb
|> se.line("pub type " <> name <> " {")
|> se.indent(1, name <> "(" <> string.join(field_strs, ", ") <> ")")
|> se.line("}")
|> se.blank_line()
}