nautilus-orm-codegen 0.1.4

Code generator for Nautilus ORM schema files
Documentation

nautilus-codegen

Code generator that reads a validated .nautilus schema IR and emits ready-to-use client code for Rust and Python.


Purpose

nautilus-codegen takes a SchemaIR produced by nautilus-schema and writes:

  • Rust — a full crate with typed model structs, enum definitions, and CRUD builders (Create, Find, FindMany, Update, Delete, CreateMany) that call into nautilus-core and nautilus-connector.
  • Python — a package with Pydantic-compatible models, async client code, and a bundled runtime (_engine.py, _client.py, _protocol.py, _errors.py) that communicates with the nautilus-engine binary over stdio.

Public API

Symbol Kind Description
resolve_schema_path(schema) fn Auto-detect the first .nautilus file in the CWD, or return the explicit path.
parse_schema(source) fn Lex + parse raw schema source text into an Ast.
generate_command(path, opts) fn Full pipeline: read → parse → validate → generate → write.
validate_command(path) fn Read, parse, and validate only — no code is written.
GenerateOptions struct Options for generate_command; see fields below.

GenerateOptions

pub struct GenerateOptions {
    /// Copy the output into site-packages (Python) or register in the
    /// workspace Cargo.toml (Rust) after generation.
    pub install: bool,
    /// Print verbose progress and IR debug output to stdout.
    pub verbose: bool,
    /// (Rust only) Also emit a `Cargo.toml` for the generated crate.
    /// When `false` (default) bare source files are written, suitable for
    /// inclusion in an existing Cargo workspace.
    pub standalone: bool,
}

Usage within the project

Two binaries use this crate as a library:

Consumer Entry point
nautilus-cli crates/nautilus-cli/src/commands/generate.rs — the unified nautilus CLI
nautilus-codegen (binary) crates/nautilus-codegen/src/main.rs — the standalone code-gen binary

Both construct a GenerateOptions value from their CLI flags and call nautilus_codegen::generate_command(&path, options).

Downstream crates that consume the generated output (not this crate directly):

  • nautilus-connector — the generated Rust builders call its NautilusClient trait.
  • nautilus-core — the generated builders construct Select, Update, Delete, and Insert values defined here.

Architecture

Template registries

Code generation is driven by two independent Tera template registries, each initialised once via std::sync::LazyLock:

Static Module Templates
generator::TEMPLATES src/generator.rs Everything under templates/rust/
python::generator::PYTHON_TEMPLATES src/python/generator.rs Everything under templates/python/

Both registries expose a private render(template_name, ctx) helper that terminates the process on a Tera error rather than propagating Result through every call site — generation errors at this stage always indicate a bug in the template, not a user error.

is_optional in FieldContext

When building the context for update.tera and related templates, each field carries an is_optional: bool flag. A field is optional when it is neither required nor an array — i.e., it maps to Option<T> in the generated UpdateInput struct. The update.tera template uses this flag to emit the correct match arm for the outer Option (skip vs. set NULL vs. set a concrete value).

create_many — batch INSERT

The create_many.tera template generates a single multi-row INSERT INTO … VALUES (…), (…) RETURNING * statement for dialects that support RETURNING (Postgres, SQLite). For MySQL, which lacks RETURNING, it falls back to a per-row loop through the single-item Create builder.

Python runtime — async subprocess I/O

The Python runtime files (_engine.py, _client.py) use asyncio.create_subprocess_exec and the resulting asyncio.subprocess.Process streams (stdin, stdout, stderr) throughout. There is no subprocess.Popen, no loop.run_in_executor, and no get_event_loop() call. This ensures full compatibility with running event loops (e.g., inside a Jupyter notebook or asyncio.run), where asyncio.get_event_loop() is deprecated or raises DeprecationWarning.

Static runtime files

Two Python runtime files contain no Tera variables. Their generators (generate_errors_init, generate_internal_init) return &'static str via include_str! rather than rendering them through the Tera engine, avoiding the overhead of a template parse + render cycle for files that never change.

Cargo.toml.tpl

templates/rust/Cargo.toml.tpl is processed by simple str::replace in writer.rs, not by Tera. The .tpl extension reflects this — it is not a Tera template and is therefore never registered in TEMPLATES.