Sign in

    This version of the module is deprecated: Moved to moonbitstack/moonctl.

    moonctl

    moonctl (mctl) — a spec-driven code generator for MoonBit (← goctl): parse a .api service spec and emit compilable moonapi scaffolding (routes + handler stubs).

    goctl
    codegen
    scaffold
    api
    moonapi
    moonbit
    cli
    Download zip
    Author
    Version
    0.6.2
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    74

    #moonctl (mctl)

    A spec-driven code generator for MoonBit — ← goctl.

    Check and Test License mooncakes

    moonctl parses a .api service spec and emits compilable moonapi scaffolding — the role goctl plays for Go. It's pure logic (parse → generate text), so it has no runtime dependencies and runs on every backend.

    #From spec to code

    Given this .api spec:

    service greet { get /ping ping "health check" get /users/:id get_user post /users create_user "create a user" }

    generate(parse(spec)) emits:

    // Code generated by moonctl. DO NOT EDIT.

    ///|
    /// Build the greet application with its routes wired to handlers.
    pub fn build_app() -> @moonapi.App {
    let app = @moonapi.App::new()
    app.get("/ping", ctx => ping(ctx), summary="health check")
    app.get("/users/:id", ctx => get_user(ctx))
    app.post("/users", ctx => create_user(ctx), summary="create a user")
    app
    }

    ///|
    pub fn ping(_ctx : @moonapi.Context) -> @moonasgi.Response {
    @moonapi.text(200, "TODO: ping")
    }
    // ... one stub per handler

    Each route registers its handler through an arrow closure. moonapi's ApiHandler is a raising fn-type, and under moonc 0.10.5 a pure named function no longer coerces to one; a closure infers the raise effect, so both a stub that never raises and a filled-in handler that raises an HttpException register unchanged.

    The generated output is verified to compile against real moonapi + moonasgi — a code generator whose output doesn't build isn't a code generator.

    #Usage

    As a library:

    let spec = @moonctl.parse(source) // -> Spec { service, routes, types }
    let code = @moonctl.generate(spec) // -> compilable moonapi scaffold (String)

    As the mctl command-line tool (native binary):

    $ mctl gen api greet.api # -> greet.mbt (moonapi scaffold) $ mctl gen proto greet.proto # -> greet_grpc.mbt (moonrpc service stub) $ mctl gen model user.api # -> user_model.mbt (moonorm model + migration) $ mctl gen crud schema.sql # -> schema_model.mbt (moonorm model + typed CRUD) $ mctl gen doc greet.api # -> greet_openapi.json + greet_swagger.html $ mctl model datasource sqlite:shop.db # -> datasource_model.mbt (read a live DB's schema) $ mctl api new blog # scaffold a moonapi service project under blog/ $ mctl rpc new echo # scaffold a moonrpc service project under echo/ $ mctl model new account # scaffold a moonorm data-layer project under account/ $ mctl docker greet # -> Dockerfile + .dockerignore $ mctl kube greet # -> deploy/deployment.yaml + deploy/service.yaml $ mctl plugin ./my-plugin greet.api # run an external plugin over the parsed spec

    generate works as a synonym for gen, and rpc for proto.

    #Cross-repo codegen

    Like goctl, mctl is the code generator for the whole stack, not just the web tier. Two more front-ends feed the sibling libraries:

    • .proto → moonrpc: parse_proto reads a proto3 file (service / rpc / message, including stream, repeated, and map<K, V>); generate_grpc emits a struct per message, a @moonrpc.Method descriptor per RPC (its gRPC :path is /package.Service/Method), a <Service>Server handler-registration struct (one synchronous-core handler per RPC, returning its reply or a gRPC @moonrpc.Status), and a <service>_methods() listing.

    • .api type → moonorm: generate_model turns each storable type block into a model struct, a <table>_model : @moonorm.Model[T] (declared columns — an id column is the primary key — plus the from_row decoder and to_columns projection that MoonBit can't synthesise for want of reflection), a <table>_table : @moonorm.Table descriptor, and a <table>_up / <table>_down migration pair (create the table idempotently, drop it — the explicit stand-in for an Alembic revision). A block with a non-storable field (a slice, map, or nested message) still gets its struct and a plain Table descriptor. The output compiles against moonorm + moondb, so a consumer imports both.

    Both outputs are verified to compile against the real moonrpc / moonorm in a scratch consumer project (moon check → rc 0), the same guarantee the moonapi scaffold carries.

    let stub = @moonctl.generate_grpc(@moonctl.parse_proto(proto_src)) // -> moonrpc stub
    let model = @moonctl.generate_model(@moonctl.parse(api_src)) // -> moonorm model + migration

    #SQL DDL → CRUD

    goctl model mysql ddl reads a .sql schema and generates a model with typed CRUD; parse_ddl + generate_crud are the MoonBit version. parse_ddl reads CREATE TABLE statements — column types, column- and table-level primary keys, NOT NULL, and DEFAULT clauses — and generate_crud emits, per table, the record struct, a @moonorm.Model, an up/down migration pair, and typed CRUD: <table>_insert and <table>_all always, plus <table>_find_by_id, <table>_update, and <table>_delete_by_id when the table has a single-column primary key. SQL types map onto the @moondb.Value set — INTEGER/BIGINT → Int/Int64, TEXT → String, REAL → Double, BLOB → Bytes, BOOLEAN → Bool. Every generated statement is parameterised, so a CRUD call binds its id and columns rather than splicing them.

    let code = @moonctl.generate_crud_from_ddl(sql_src) // -> moonorm model + CRUD (String)

    The output is verified to compile against the real moonorm + moondb in a scratch consumer (moon check → rc 0).

    #Live datasource reflection

    goctl model … datasource connects to a running database, reads its schema, and generates models. mctl model datasource <dsn> is the MoonBit version. A SQLite DSN reads sqlite_master + PRAGMA table_info through the FFI driver; a PostgreSQL DSN reads information_schema over the async wire protocol. Both produce the same neutral ReflectedColumn list, which folds into the same generate_crud pipeline the .sql front end uses, so a live schema and a .sql file yield an identical model.

    $ mctl model datasource sqlite:shop.db $ mctl model datasource postgres://user:pass@localhost:5432/shop

    DSN parsing (parse_dsn) and the reflected-column → model fold (tables_from_reflection / generate_crud_from_reflection) are pure, all-backend core in the library; the schema reading lives in the native reflect sub-package, so the library itself stays dependency-free. The pipeline is verified end to end against a real in-memory SQLite database (create tables → reflect → generate → the generated models compile against moonorm + moondb).

    #Project scaffolds

    Like goctl's api new / rpc new / docker / kube, mctl scaffolds whole nested project trees, not just single files:

    $ mctl api new blog # blog/{moon.mod.json, blog.api, src/{moon.pkg.json, app.mbt}, README.md} $ mctl rpc new echo # echo/{moon.mod.json, echo.proto, src/{moon.pkg.json, service.mbt}, README.md} $ mctl model new account # account/{moon.mod.json, schema.sql, src/{moon.pkg.json, model.mbt}, README.md} $ mctl docker greet # Dockerfile (two-stage native build) + .dockerignore $ mctl kube greet # deploy/deployment.yaml + deploy/service.yaml

    The api, rpc and model skeletons emit their source through the same built-in generators the gen commands use, seeded from a sample spec, so a fresh scaffold compiles against the published libraries out of the box (moon check → rc 0, no warnings). Each returns its files as GenFile { path, content } — the same shape the plugin protocol uses — which the mctl binary writes out, creating nested directories as it goes:

    let files = @moonctl.scaffold_api("blog") // -> Array[GenFile] to write

    #Plugins

    Like goctl api plugin, mctl plugin <exe> <file.api> drives an out-of-tree generator off the same parsed spec the built-in generators use. mctl parses the .api, writes the spec as JSON to the plugin's stdin, and the plugin prints the files it wants written — a JSON {"files": [{"path", "content"}, …]} reply — on its stdout, which mctl writes to disk. The wire shapes are spec_to_json / plugin_request (request) and parse_gen_files (reply); a plugin written in MoonBit reads its input with spec_from_json and writes its output with gen_files_to_json. cmd/demo-plugin is a working example that emits a route listing.

    let req = @moonctl.plugin_request(spec) // JSON for the plugin's stdin
    let files = @moonctl.parse_gen_files(@json.parse(out)) // the plugin's stdout -> files to write

    #OpenAPI / Swagger

    generate_doc emits an OpenAPI document straight from a .api spec — routes become paths (a :id segment turns into {id}, with a typed path parameter), and type blocks become component schemas. It speaks every mainstream dialect, the same three moonapi's runtime emitter does, so a generated service and a hand-built app document the same way.

    let spec = @moonctl.parse(api_src)
    let json = @moonctl.generate_doc(spec, version=OpenApi31) // or Swagger20 / OpenApi30
    let html = @moonctl.swagger_ui_stub(spec_url="/openapi.json")

    #Template engine

    moonctl ships a runtime template engine — the faithful equivalent of goctl's text/template. Because MoonBit has no reflection, template data is an explicit tagged Value (Str/Int/Bool/List/Dict/…), the way encoding/json models dynamic data. It supports {{.Field}} interpolation, {{if}}/{{else if}}/{{else}}/{{end}}, {{range}} (with $index, $value binding) and {{with}}, $variable assignment, | pipelines with parenthesised sub-pipelines, {{- -}} whitespace trimming, {{/* comments */}}, and a function library (upper, lower, title, trim, trimPrefix/trimSuffix, hasPrefix/hasSuffix, contains, replace, eq/ne/lt/le/gt/ge, and/or/not, len, index, default, printf/print/println, plus your own via func).

    let out = @moonctl.render(
    "{{range .routes}}{{.verb}} {{.path}} -> {{.handler}}\n{{end}}",
    @moonctl.spec_to_value(spec),
    )
    // or drive codegen from a caller-supplied template:
    let code = @moonctl.generate(spec, template=my_template) // == generate_with

    #Status & roadmap (transliterating goctl)

    Shipped: the .api parser (service blocks, verb path handler "summary" routes, typed type blocks → request/response schemas, // comments), the moonapi generator (verified to compile against real moonapi), the runtime template engine (goctl's text/template equivalent), the cross-repo generators — .proto → moonrpc service stubs, .api type → moonorm models (struct + Model + Table + up/down migration), SQL DDL → moonorm models with typed CRUD, and .api → OpenAPI (Swagger 2.0 / 3.0 / 3.1) + a Swagger UI stub — with the generated model and CRUD verified to compile against the real moonorm + moondb and the generated document verified to parse — the plugin system (an external generator driven off the parsed spec over stdio JSON, with a working demo plugin), live-datasource schema reflection (model datasource — read a running SQLite or PostgreSQL schema and generate models, verified end to end against a real SQLite database), the nested project scaffolds (api/rpc/model new, docker, kube), and the mctl gen api/proto/model/crud/doc + mctl model datasource + mctl plugin CLI binary (native). Generated moonapi routing registers each handler through a raising arrow closure, so a scaffold compiles under moonc 0.10.5 (where a pure named handler no longer coerces to moonapi's raising ApiHandler). All verified across every backend (0 warnings under --deny-warn). Next, feature-by-feature: a Redis-cached CRUD variant.

    #License

    Apache-2.0.

    FuncFn

    type FuncFn = (Array[Value]) -> Value raise TemplateError

    A pipeline function: it receives the already-evaluated argument list (with the piped-in value, if any, appended last, mirroring Go) and returns a Value.

    DataSourceError

    pub suberror DataSourceError {
    BadDsn(String)
    }

    Raised when a DSN string cannot be parsed into a DataSource.

    TemplateError

    pub suberror TemplateError {
    ParseError(String)
    ExecError(String)
    }

    A template parse or execution failure, carrying a human-readable message.

    DataSource

    pub(all) enum DataSource {
    Sqlite(String)
    Postgres(PgTarget)
    }

    A parsed datasource DSN: which backend to read, and how to reach it.

    DdlColumn

    pub(all) struct DdlColumn {
    name : String
    type_ : String
    primary_key : Bool
    nullable : Bool
    default_ : String?
    }

    One parsed column of a CREATE TABLE statement: the SQL column name, the MoonBit type_ its SQL type maps to (Int/Int64/String/Double/Bool/ Bytes), whether it is a primary key, whether it accepts NULL, and its literal DEFAULT clause if any (kept as source text — it is emitted into DDL comments, never bound).

    DdlTable

    pub(all) struct DdlTable {
    name : String
    columns : Array[DdlColumn]
    }

    A parsed CREATE TABLE: the SQL table name and its ordered columns.

    DocVersion

    pub(all) enum DocVersion {
    Swagger20
    OpenApi30
    OpenApi31
    } derive(Eq)

    Target OpenAPI / Swagger document version. mctl emits every mainstream version from one .api spec — the same shape moonapi's runtime emitter takes, so a generated service and a hand-built moonapi app document the same way.

    Field

    pub(all) struct Field {
    name : String
    type_ : String
    tag : String
    }

    One field of a type block: its name, the (already MoonBit-mapped) type, and the raw back-tick struct tag (json:"…"/path/form/header, empty when absent) that carries the wire name and binding location.

    Field::json_name

    fn Field::json_name(self : Field) -> String

    The field's serialized name: the json struct-tag name (with any ,optional/,omitempty option stripped) when the tag carries one, else the field's own name.

    GenFile

    pub(all) struct GenFile {
    path : String
    content : String
    }

    One file a plugin asks mctl to write: a destination path (relative to the output directory) and its full content.

    PgTarget

    pub(all) struct PgTarget {
    host : String
    port : Int
    user : String
    password : String
    database : String
    }

    A PostgreSQL connection target parsed from a postgres:// DSN.

    Proto

    pub(all) struct Proto {
    package_ : String
    services : Array[ProtoService]
    messages : Array[ProtoMessage]
    enums : Array[ProtoEnum]
    }

    A parsed .proto file: its package (empty when none) and the services and messages it declares.

    ProtoEnum

    pub(all) struct ProtoEnum {
    name : String
    values : Array[ProtoEnumValue]
    }

    A protobuf enum declaration, generated into a MoonBit enum plus an integer mapping (proto3 enums are int-backed, and the value numbered 0 is the default).

    ProtoEnumValue

    pub(all) struct ProtoEnumValue {
    name : String
    number : Int
    }

    One value of a protobuf enum: its name and its integer number.

    ProtoField

    pub(all) struct ProtoField {
    name : String
    type_ : String
    number : Int
    }

    One field of a protobuf message: its name, the (already MoonBit-mapped) type, and its wire number (the = N). repeated becomes Array[T] and map<K, V> becomes Map[K, V].

    ProtoMessage

    pub(all) struct ProtoMessage {
    name : String
    fields : Array[ProtoField]
    oneofs : Array[ProtoOneof]
    reserved_numbers : Array[Int]
    reserved_names : Array[String]
    }

    A protobuf message declaration, generated into a MoonBit struct. oneof groups are lifted out of fields into oneofs; reserved field numbers and names are recorded so a reused one can be caught.

    ProtoOneof

    pub(all) struct ProtoOneof {
    name : String
    variants : Array[ProtoField]
    }

    A protobuf oneof group: its name and the members (exactly one may be set). Generated into a MoonBit enum — the faithful equivalent of the "exactly one" invariant, which flattening to optional fields would lose.

    ProtoService

    pub(all) struct ProtoService {
    name : String
    rpcs : Array[Rpc]
    }

    A protobuf service declaration and its RPC methods.

    ReflectedColumn

    pub(all) struct ReflectedColumn {
    table : String
    name : String
    sql_type : String
    primary_key : Bool
    nullable : Bool
    }

    One column of a reflected database schema: the table it belongs to, its name, its raw SQL type text (INTEGER, character varying, VARCHAR(255), …), and whether it is a primary key / accepts NULL. This is the neutral shape a live reader (SQLite PRAGMA table_info, PostgreSQL information_schema.columns) produces and tables_from_reflection folds into DdlTables.

    Route

    pub(all) struct Route {
    verb : String
    path : String
    handler : String
    summary : String
    req : String
    resp : String
    }

    One route in a service spec: HTTP verb, path pattern, handler name, and an optional summary. A modern goctl route (verb /path (Req) returns (Resp)) also carries its request/response type names (its @doc fills summary); both are empty for the legacy inline form (verb /path handler "summary").

    Rpc

    pub(all) struct Rpc {
    name : String
    request : String
    response : String
    client_streaming : Bool
    server_streaming : Bool
    }

    One rpc method of a service: its name, request and response message names, and whether either side is a stream (client/server/bidi streaming).

    Spec

    pub(all) struct Spec {
    service : String
    routes : Array[Route]
    types : Array[TypeDef]
    info : Array[(String, String)]
    }

    A parsed .api service specification.

    Template

    pub struct Template {
    renderer : (Value) -> String raise TemplateError
    funcs : Map[String, (Array[Value]) -> Value raise TemplateError]
    }

    A parsed, reusable template — moonctl's text/template equivalent. Build it with Template::new, register extra functions with func, parse a source with parse, then render it against a Value any number of times. The parsed node tree stays private, captured inside renderer.

    Template::func

    fn Template::func(self : Template, name : String, f : (Array[Value]) -> Value raise TemplateError) -> Template

    Register (or override) a pipeline function, returning self for chaining — the analogue of Go's Template.Funcs. Register before parse.

    Template::new

    fn Template::new() -> Template

    A fresh template preloaded with the default function library. Rendering before parse yields the empty string.

    Template::parse

    fn Template::parse(self : Template, source : String) -> Template raise TemplateError

    Parse source into this template's node tree, returning self for chaining. Raises ParseError on a syntax error (unclosed action, dangling {{end}}, …).

    Template::render

    fn Template::render(self : Template, data : Value) -> String raise TemplateError

    Render the template against data, producing the output string. Raises ExecError on an execution fault (undefined variable/function, bad range target, …).

    TypeDef

    pub(all) struct TypeDef {
    name : String
    fields : Array[Field]
    }

    A named message schema declared by a type Name { field: Type … } block, generated into a MoonBit struct (goctl's request/response types).

    Value

    pub(all) enum Value {
    Null
    Bool(Bool)
    Int(Int64)
    Float(Double)
    Str(String)
    List(Array[Value])
    Dict(Map[String, Value])
    }

    Dynamic template data. .Field lookups resolve against Dict; range iterates List (index/element) or Dict (sorted key/value). This is the explicit stand-in for the arbitrary Go values goctl reflects over.

    gen_files_to_json

    fn gen_files_to_json(files : Array[GenFile]) -> Json

    Serialise a list of generated files into the plugin-reply JSON — the writer a MoonBit plugin uses on its stdout. Emits the {"files": [...]} envelope.

    generate

    fn generate(spec : Spec, template? : String) -> String raise TemplateError

    Generate compilable moonapi scaffolding from a spec: a MoonBit struct for every type block, a build_app that wires every route to its handler, plus a stub for each handler. The routing part depends only on Lfan-ke/moonapi and Lfan-ke/moonasgi; the emitted schemas are dependency-free.

    Pass template to render the spec through the runtime template engine (see generate_with) instead of the built-in generator; omit it for the default scaffold.

    generate_agent

    fn generate_agent(spec : Spec) -> String

    Generate a moonkoog agent scaffold from spec. Every route becomes a Tool whose descriptor advertises the operation (a single string input parameter to fill out) and whose execute_raw is a stub, and build_agent assembles them into an AIAgent with the service's name in its system prompt. The output compiles against Lfan-ke/moonkoog; fill in each execute_raw to make it run.

    generate_crud

    fn generate_crud(tables : Array[DdlTable]) -> String

    Generate a moonorm data-access layer from parsed DDL: for every table, the record struct, its @moonorm.Model (columns + from_row + to_columns), a @moonorm.Table descriptor, an up/down migration pair, and typed CRUD (insert/all, plus find_by_id/update/delete_by_id when the table has a single-column primary key). The output compiles against Lfan-ke/moonorm + Lfan-ke/moondb; a consuming package imports both. This is the .sql-schema counterpart of goctl's model mysql ddl.

    generate_crud_from_ddl

    fn generate_crud_from_ddl(source : String) -> String

    Parse a SQL DDL script and generate its moonorm data-access layer in one step.

    generate_crud_from_reflection

    fn generate_crud_from_reflection(cols : Array[ReflectedColumn]) -> String

    Generate a moonorm data-access layer (models + typed CRUD) straight from a live schema's reflected columns — the in-memory counterpart of generate_crud_from_ddl. The reflect sub-package produces the ReflectedColumns from a real connection.

    generate_doc

    fn generate_doc(spec : Spec, version? : DocVersion, title? : String, api_version? : String) -> String

    Generate an OpenAPI / Swagger document from spec, stringified with two-space indentation. version picks the dialect (Swagger 2.0 / OpenAPI 3.0 / 3.1).

    generate_gql

    fn generate_gql(spec : Spec) -> String

    Generate a moongql code-first schema and resolver stubs from spec's type blocks. Each type becomes an object type with its fields mapped to GraphQL types; a Query root gains a <type>(id: ID!): <Type> lookup per type; and every field gets a resolver stub. The output compiles against Lfan-ke/moongql; fill in the stub bodies to make it run.

    generate_grpc

    fn generate_grpc(proto : Proto) -> String

    Generate a moonrpc service stub from a parsed .proto: a MoonBit struct per message, a @moonrpc.Method descriptor per RPC (its gRPC :path is /package.Service/Method), a <Service>Server handler-registration struct (one synchronous-core handler field per RPC, each returning its reply or a gRPC @moonrpc.Status), and a <service>_methods() listing. The output compiles against Lfan-ke/moonrpc.

    generate_model

    fn generate_model(spec : Spec) -> String

    Generate a moonorm data layer for every type block in spec. A block whose fields are all storable yields: the model struct; a <table>_model : @moonorm.Model[T] built with @moonorm.Model::new — declared columns (an id column is the primary key), a from_row decoder, and the to_columns projection an INSERT binds; a <table>_table : @moonorm.Table descriptor; and a <table>_up / <table>_down migration pair (create the table idempotently / drop it). This is the explicit MoonBit stand-in for a SQLAlchemy declarative class plus an Alembic revision, which reflection would otherwise synthesise. A block with a non-storable field (a slice, map, or nested message) still gets its struct and a plain @moonorm.Table descriptor.

    The output compiles against Lfan-ke/moonorm + Lfan-ke/moondb; a consuming package imports both.

    generate_with

    fn generate_with(spec : Spec, template_source : String) -> String raise TemplateError

    Generate code from a Spec with a caller-supplied template instead of the built-in generator — the runtime-template path goctl exposes for custom scaffolds. Equivalent to render(template_source, spec_to_value(spec)).

    openapi_document

    fn openapi_document(spec : Spec, version? : DocVersion, title? : String, api_version? : String) -> Json

    Build the OpenAPI / Swagger document for spec as a Json value. Routes fold into paths → HTTP method → operation (with operationId, any :name path parameters, and a 200 response); every type block becomes a component schema (definitions in 2.0, components/schemas in 3.x). version selects the document dialect; title/api_version fill the info block.

    parse

    fn parse(source : String) -> Spec

    Read a goctl-style .api description into a Spec: the service name, its routes, the type blocks, and the info block. A line that matches nothing is skipped rather than raising — a spec is written by hand, and half of one should still generate what it does describe.

    parse_ddl

    fn parse_ddl(source : String) -> Array[DdlTable]

    Parse a SQL DDL script into its CREATE TABLE definitions. Recognises CREATETABLE [IF NOT EXISTS] name ( … ) (quoted or bare name), skipping any other statement. Column types, primary keys (column- or table-level), NOT NULL and DEFAULT clauses are captured; everything needed to emit a moonorm model.

    parse_dsn

    fn parse_dsn(dsn : String) -> DataSource raise DataSourceError

    Parse a datasource DSN into a DataSource. Recognised forms:

    • SQLite: sqlite:PATH, sqlite://PATH, sqlite3:PATH, file:PATH, the literal :memory:, or a bare path ending in .db / .sqlite / .sqlite3.
    • PostgreSQL: postgres://[user[:password]@]host[:port][/database][?…] (and the postgresql:// spelling). The port defaults to 5432, the user to postgres; the database name is required.

    Raises BadDsn on an unrecognised scheme or a PostgreSQL URL missing its host or database.

    parse_gen_files

    fn parse_gen_files(j : Json) -> Array[GenFile]

    Parse a plugin's stdout into the files to write. Two shapes are accepted: a bare array [{path, content}, …], or an object {"files": [ … ]} (goctl-style envelope). An element missing path or content is skipped. The list preserves the plugin's order.

    parse_proto

    fn parse_proto(source : String) -> Proto

    Parse a minimal .proto (proto3). Recognises the package declaration, service blocks with their rpc methods (including stream), message blocks with scalar/repeated/map fields, and top-level enum declarations. syntax, import, and option are skipped.

    syntax = "proto3"; package greet; service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); } message HelloRequest { string name = 1; } message HelloReply { string message = 1; }

    plugin_request

    fn plugin_request(spec : Spec) -> String

    The plugin request as a two-space-indented JSON string, ready to write to the plugin's stdin.

    proto_reserved_conflicts

    fn proto_reserved_conflicts(m : ProtoMessage) -> Array[String]

    The names of m's fields (oneof members included) that reuse a reserved field number or name — protobuf forbids reusing either, and this surfaces an accidental reuse. Empty when the message is clean.

    render

    fn render(source : String, data : Value) -> String raise TemplateError

    Parse and render source against data in one call, with the default function library.

    scaffold_api

    fn scaffold_api(name : String) -> Array[GenFile]

    Scaffold a runnable moonapi service project under <name>/: a moon.mod.json (depending on the published moonapi + moonasgi), a sample <name>.api spec, its generated routes+handlers in src/app.mbt (through the same generate the gen api command uses, so it compiles), and a README. This is goctl's goctl api new.

    scaffold_docker

    fn scaffold_docker(name : String, port? : Int) -> Array[GenFile]

    Scaffold container files for a native mctl-generated service: a Dockerfile (a MoonBit build stage that produces the native binary, then a slim runtime stage that runs it) and a .dockerignore. name is the binary/image name, port the port the service listens on. goctl's goctl docker.

    scaffold_kube

    fn scaffold_kube(name : String, port? : Int, replicas? : Int) -> Array[GenFile]

    Scaffold a Kubernetes deployment for the service under deploy/: a Deployment (replicas pods of the <name> image, a container port, and a liveness probe on /ping) and a Service exposing it. goctl's goctl kube deploy.

    scaffold_model

    fn scaffold_model(name : String) -> Array[GenFile]

    Scaffold a moonorm data-layer project under <name>/: a moon.mod.json (depending on the published moonorm + moondb), a sample schema.sql, its generated models + CRUD in src/model.mbt (through the same generate_crud_from_ddl as gen crud), and a README. goctl's goctl model … new.

    scaffold_rpc

    fn scaffold_rpc(name : String) -> Array[GenFile]

    Scaffold a moonrpc service project under <name>/: a moon.mod.json (depending on the published moonrpc), a sample <name>.proto, its generated service stub in src/service.mbt (through the same generate_grpc as gen proto), and a README. goctl's goctl rpc new.

    spec_from_json

    fn spec_from_json(j : Json) -> Spec

    Reconstruct a Spec from the request JSON — the reader a plugin written in MoonBit uses on its stdin. Missing or mistyped members degrade to empty rather than raising, so a partial request still parses.

    spec_to_json

    fn spec_to_json(spec : Spec) -> Json

    Serialise a Spec into the plugin request JSON. The shape mirrors the Spec itself — service, routes (verb/path/handler/summary), and types (name plus fields of name/type) — so a plugin in any language parses it with an ordinary JSON reader. type_ is written as type, the name a caller expects.

    spec_to_value

    fn spec_to_value(spec : Spec) -> Value

    Expose a parsed .api Spec as template Value data: a Dict with service (string), routes (list of {verb, path, handler, summary,hasSummary}), types (list of {name, fields:[{name, type}]}) and hasTypes (bool). This is what the template-driven generator ranges over.

    swagger_ui_stub

    fn swagger_ui_stub(spec_url? : String, title? : String) -> String

    A self-contained Swagger UI page for the document served at spec_url. The same stub moonapi ships, so a generated service and a live app render alike.

    tables_from_reflection

    fn tables_from_reflection(cols : Array[ReflectedColumn]) -> Array[DdlTable]

    Fold reflected columns into DdlTables, grouping by table in first-seen order and mapping each raw SQL type onto its MoonBit scalar (the leading type keyword drives the mapping, so character varying and VARCHAR(255) both land on String). The result feeds generate_crud, so a live schema and a .sql file reach the same model generator.