Language-neutral Smithy code generation core
| Part | Types and functions | Purpose |
|---|---|---|
| Symbols | Symbol, SymbolName, Namespace, OutputPath, PackageName, SymbolDependency, SymbolReference | Name a generated item, its file and its dependencies. Each type has a checked parse constructor. |
| Symbol provider | SymbolProvider, ReservedWords | Map a shape to a Symbol and a member to a name. Decorators and a cache add behavior. |
| Shape order | service_closure, topological_order | List the shapes of a service and order them by their dependencies. |
| Code writer | CodeWriter, TemplateValue, Section, Interceptor | Write text with templates, indentation, imports and sections. |
| Import policies | ImportPolicy, ImportEntry, PackageImport | Select how $T writes a foreign symbol: by name, or qualified by its package. |
| Contextual names | ServiceNames | Apply the service rename map to shape names. |
| Writer delegator | WriterDelegator | Keep one writer for each output file and collect the files. |
| Integrations | Integration[C], sort_integrations | Add behavior to a generator: ordered hooks for the model, the provider and the writers. |
| Director | DirectedCodegen[C], CreateContext, run_codegen | Walk the shapes of a service and call one function for each kind of shape. |
| Build plugin | PluginContext, build_plugin | Read the build settings and adapt a generator to the build runner. |
| Syntax | Meaning |
|---|---|
| $$ | A literal $. |
| $L, $S, $T, $C | The next argument, with a formatter. |
| $1L | The argument at position 1 (positions start at 1). |
| $name:L, ${name:L} | The value name from the context of the writer. |
| ${name:C\|} | Start each later line of the value with the leading white space of the current line of the output. It is not the column of the expression. |
| ${?x} ... ${/x} | Render the block when x is true. |
| ${^x} ... ${/x} | Render the block when x is false. |
| ${#x} ... ${/x} | Render the block for each item of the list or record x. |
| ${#x as k, v} | The same, with the names k and v for the key and the value. |
| key, value | The key and the value of the item in a loop. |
| key.first, key.last | True for the first and for the last item in a loop. |
| ${- ... -} | Remove the white space before or after the tag. |
| Formatter | Result |
|---|---|
| L | A literal: text, a number or a Boolean as it is. |
| S | A string literal, with quotes and escapes. |
| T | A symbol. The writer adds an import when the symbol is in another namespace. |
| C | Code from a Call value, written by a nested writer. |
| Policy | Text of a foreign symbol | Record | Language |
|---|---|---|---|
| ImportPolicy::by_name(), the default | The name, or the local name of its reference | imports(): one ImportEntry for each name | Python |
| ImportPolicy::by_package(qualify~, qualifier~, locals_block_qualifiers~) | qualify(qualifier, symbol), such as rpc.Decimal or @rpc.Decimal | package_imports(): one PackageImport for each package | Go, MoonBit |
///|
test {
let last_segment = (ns : @codegen.Namespace) => {
let parts = ns.text.split("/").map(part => part.to_owned()).collect()
@codegen.SymbolName::parse(parts[parts.length() - 1]).unwrap()
}
let writer = @codegen.CodeWriter::new(
ns=@codegen.Namespace::parse("example/pos", separator="/").unwrap(),
imports=@codegen.ImportPolicy::by_package(
qualify=(qualifier, symbol) => {
"@\{qualifier.to_string()}.\{symbol.name.to_string()}"
},
qualifier=last_segment,
locals_block_qualifiers=false,
),
)
let decimal = @codegen.Symbol::new(
@codegen.SymbolName::parse("Decimal").unwrap(),
ns=@codegen.Namespace::parse("example/pos/rpc", separator="/").unwrap(),
)
writer.write("let amount : $T = $T::zero()", args=[
@codegen.Sym(decimal),
@codegen.Sym(decimal),
])
assert_eq(
writer.to_string(),
"let amount : @rpc.Decimal = @rpc.Decimal::zero()\n",
)
assert_eq(
writer.package_imports().map(entry => entry.qualifier.to_string()),
["rpc"],
)
}///|
test {
let item = @codegen.Symbol::new(
@codegen.SymbolName::parse("Item").unwrap(),
ns=@codegen.Namespace::parse("example.items", separator=".").unwrap(),
)
let order = @codegen.Symbol::new(
@codegen.SymbolName::parse("Order").unwrap(),
ns=@codegen.Namespace::parse("example.orders", separator=".").unwrap(),
)
let writer = @codegen.CodeWriter::new(ns=order.ns)
writer.on_section(
@codegen.Interceptor::appender("header", (w, _) => w.write("// generated")),
)
writer.inject_section(@codegen.Section::new("header"))
writer.put_context("members", @codegen.Record({ "item": @codegen.Sym(item) }))
writer.write(
"structure $T {\n${#members}\n ${key:L}: ${value:T}\n${/members}\n}",
args=[@codegen.Sym(order)],
)
assert_eq(
writer.to_string(),
"// generated\nstructure Order {\n item: Item\n}\n",
)
assert_eq(writer.imports().map(entry => entry.name.to_string()), ["Item"])
}{
"version": "1.0",
"sources": ["model"],
"plugins": {
"codegenFixture": {
"service": "example.orders#Orders",
"header": "generated"
}
}
}smithy build --config smithy-build.jsonpub struct CodeWriter {
// private fields
}fn CodeWriter::new(ns? : Namespace, indent_text? : String, string_literal? : (String) -> String, imports? : ImportPolicy) -> CodeWriterfn CodeWriter::open_block(self : CodeWriter, open : String, close : String, args? : Array[TemplateValue], body : (CodeWriter) -> Unit raise CodegenError) -> Unit raise CodegenErrorfn CodeWriter::put_formatter(self : CodeWriter, character : Char, formatter : (CodeWriter, TemplateValue) -> String raise CodegenError) -> Unitfn CodeWriter::write(self : CodeWriter, template : String, args? : Array[TemplateValue]) -> Unit raise CodegenErrorfn CodeWriter::write_inline(self : CodeWriter, template : String, args? : Array[TemplateValue]) -> Unit raise CodegenErrorpub struct CreateContext {
model : Model
plugin : PluginContext
service : Shape
provider : SymbolProvider
integrations : Array[String]
}pub struct DirectedCodegen[C] {
// private fields
}fn[C] DirectedCodegen::new(create_symbol_provider~ : (Model, PluginContext) -> SymbolProvider, create_context~ : (CreateContext) -> C, delegator~ : (C) -> WriterDelegator, generate_service~ : (C, Shape) -> Unit raise CodegenError, generate_structure~ : (C, Shape) -> Unit raise CodegenError, generate_error~ : (C, Shape) -> Unit raise CodegenError, generate_union~ : (C, Shape) -> Unit raise CodegenError, generate_enum~ : (C, Shape) -> Unit raise CodegenError, generate_int_enum~ : (C, Shape) -> Unit raise CodegenError, generate_operation? : (C, Shape) -> Unit raise CodegenError, generate_resource? : (C, Shape) -> Unit raise CodegenError, generate_list? : (C, Shape) -> Unit raise CodegenError, generate_map? : (C, Shape) -> Unit raise CodegenError, customize_before_integrations? : (C) -> Unit raise CodegenError, customize_after_integrations? : (C) -> Unit raise CodegenError) -> DirectedCodegen[C]pub(all) struct ImportEntry {
ns : Namespace
name : SymbolName
local_name : SymbolName?
} derive(Compare, Eq, Hash, Debug)pub struct ImportPolicy {
// private fields
}fn ImportPolicy::by_package(qualify~ : (SymbolName, Symbol) -> String, qualifier~ : (Namespace) -> SymbolName, locals_block_qualifiers~ : Bool) -> ImportPolicypub struct Integration[C] {
name : String
run_before : Array[String]
run_after : Array[String]
priority : Int
preprocess_model : (Model, PluginContext) -> Model raise CodegenError
decorate_symbol_provider : (SymbolProvider, PluginContext) -> SymbolProvider
interceptors : (C) -> Array[Interceptor]
customize : (C) -> Unit raise CodegenError
}fn[C] Integration::new(name : String, run_before? : Array[String], run_after? : Array[String], priority? : Int, preprocess_model? : (Model, PluginContext) -> Model raise CodegenError, decorate_symbol_provider? : (SymbolProvider, PluginContext) -> SymbolProvider, interceptors? : (C) -> Array[Interceptor], customize? : (C) -> Unit raise CodegenError) -> Integration[C]pub struct Interceptor {
// private fields
}fn Interceptor::appender(section_name : String, run : (CodeWriter, Section) -> Unit raise CodegenError) -> Interceptorfn Interceptor::new(section_name : String, run : (CodeWriter, Section, String) -> Unit raise CodegenError) -> Interceptorfn Interceptor::prepender(section_name : String, run : (CodeWriter, Section) -> Unit raise CodegenError) -> Interceptorimpl Compare for OutputPathpub(all) struct PackageImport {
ns : Namespace
qualifier : SymbolName
explicit : Bool
} derive(Eq, Hash, Debug)impl Compare for PackageImportimpl Compare for PackageNamefn PluginContext::check_settings(self : PluginContext, allowed : Array[String]) -> Unit raise CodegenErrorpub struct ReservedWords {
words : Set[String]
case_sensitive : Bool
escape_with : (String) -> String
}fn ReservedWords::new(words : Array[String], case_sensitive? : Bool, escape? : (String) -> String) -> ReservedWordspub struct ServiceNames {
// private fields
}fn ServiceNames::for_services(model : Model, services : Array[ShapeId]) -> ServiceNames raise CodegenErrorpub struct Symbol {
name : SymbolName
ns : Namespace
definition : OutputPath?
references : Array[SymbolReference]
dependencies : Array[SymbolDependency]
properties : Map[String, Json]
} derive(Eq, Debug)pub(all) struct SymbolDependency {
package_name : PackageName
version : String
kind : DependencyKind
} derive(Eq, Hash, Debug)impl Compare for SymbolDependencyimpl Compare for SymbolNamepub struct SymbolProvider {
symbol_fn : (Shape) -> Symbol raise CodegenError
member_fn : (Shape) -> String
}fn SymbolProvider::new(to_symbol : (Shape) -> Symbol raise CodegenError, to_member_name? : (Shape) -> String) -> SymbolProviderfn SymbolProvider::with_reserved_words(self : SymbolProvider, symbols~ : ReservedWords, members~ : ReservedWords) -> SymbolProviderpub(all) enum TemplateValue {
Null
Bool(Bool)
Int(Int)
Text(String)
Sym(Symbol)
Ref(SymbolReference)
Call((CodeWriter) -> Unit raise CodegenError)
List(Array[TemplateValue])
Record(Map[String, TemplateValue])
}pub struct WriterDelegator {
// private fields
}fn WriterDelegator::add_interceptors(self : WriterDelegator, interceptors : Array[Interceptor]) -> Unitfn WriterDelegator::new(provider : SymbolProvider, factory? : (OutputPath, Namespace) -> CodeWriter, render? : (CodeWriter) -> String) -> WriterDelegatorfn WriterDelegator::use_file_writer(self : WriterDelegator, path : OutputPath, ns : Namespace, body : (CodeWriter) -> Unit raise CodegenError) -> Unit raise CodegenErrorfn WriterDelegator::use_shape_writer(self : WriterDelegator, shape : Shape, body : (CodeWriter) -> Unit raise CodegenError) -> Unit raise CodegenErrorfn build_plugin(run : (PluginContext) -> Map[String, String] raise CodegenError) -> ((Model, Json) -> Map[String, String] raise BuildError)fn json_string_literal(text : String) -> Stringfn[C] run_codegen(plugin : PluginContext, director : DirectedCodegen[C], integrations? : Array[Integration[C]]) -> Map[String, String] raise CodegenError