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 intonautilus-coreandnautilus-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 thenautilus-enginebinary 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
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 itsNautilusClienttrait.nautilus-core— the generated builders constructSelect,Update,Delete, andInsertvalues 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.