Public API
Every generator in Ontogen is a standalone function. You call them from build.rs, pass configuration, and get back a typed result. No framework context, no trait implementations.
The functions are listed here in pipeline order — each one’s output can feed the next.
parse_schema
Section titled “parse_schema”pub fn parse_schema(config: &SchemaConfig) -> Result<SchemaOutput, CodegenError>Reads .rs files from your schema directory, finds structs annotated with #[derive(OntologyEntity)] and #[ontology(entity, ...)], and parses them into EntityDef metadata using syn.
This is always the starting point. Every other generator needs the entity list that comes out of this.
Parameters:
| Parameter | Type | Description |
|---|---|---|
config | &SchemaConfig | Points to the directory containing your schema .rs files. |
Returns: SchemaOutput containing entities: Vec<EntityDef>.
Side effects: Emits cargo:rerun-if-changed directives for every .rs file in the schema directory. When you edit an entity, Cargo re-runs build.rs automatically.
let schema = parse_schema(&SchemaConfig { schema_dir: "src/schema".into(),})?;// schema.entities is now available for all downstream generatorsgen_seaorm
Section titled “gen_seaorm”pub fn gen_seaorm( entities: &[EntityDef], config: &SeaOrmConfig,) -> Result<SeaOrmOutput, CodegenError>Generates SeaORM persistence code from your entity definitions:
- Entity modules — table models with typed columns and
#[sea_orm(...)]attributes. - Relation enums —
BelongsTo,HasMany, andRelatedimplementations for cross-entity navigation. - Junction tables — full SeaORM entities for many-to-many relationships.
- Conversion functions —
from_model()andto_active_model()methods on your schema types.
Parameters:
| Parameter | Type | Description |
|---|---|---|
entities | &[EntityDef] | Entity definitions from parse_schema. |
config | &SeaOrmConfig | Output paths for entities and conversions. |
Returns: SeaOrmOutput with metadata about generated tables, junction tables, and conversion functions. Downstream generators (especially gen_store) use this to produce exact table and column references rather than guessing from conventions.
let seaorm = gen_seaorm(&schema.entities, &SeaOrmConfig { entity_output: "src/persistence/db/entities/generated".into(), conversion_output: "src/persistence/db/conversions/generated".into(), skip_conversions: vec![],})?;gen_markdown_io
Section titled “gen_markdown_io”pub fn gen_markdown_io( entities: &[EntityDef], config: &MarkdownIoConfig,) -> Result<(), CodegenError>Generates Markdown I/O code for content-as-code workflows:
- Parser dispatch — reads a Markdown file, extracts YAML frontmatter, and deserializes it into your schema type.
- Writers — serialize your schema type back to Markdown with YAML frontmatter.
- Filesystem operations — read/write entities as Markdown files on disk, with directory-based organization.
This generator is a sibling of gen_seaorm — it consumes SchemaOutput independently and produces no output struct. Use it when your entities live in Markdown files (wikis, knowledge bases, content repos) rather than (or in addition to) a database.
Parameters:
| Parameter | Type | Description |
|---|---|---|
entities | &[EntityDef] | Entity definitions from parse_schema. |
config | &MarkdownIoConfig | Output directory for generated code. |
Returns: (). This generator only writes files; it produces no IR for downstream consumption.
gen_markdown_io(&schema.entities, &MarkdownIoConfig { output_dir: "src/persistence/markdown".into(),})?;gen_dtos
Section titled “gen_dtos”pub fn gen_dtos( entities: &[EntityDef], config: &DtoConfig,) -> Result<(), CodegenError>Generates CreateEntityInput and UpdateEntityInput structs — the input types for create and update operations.
For each entity, you get:
CreateEntityInput— all fields exceptid(if auto-generated). Required fields are required; optional fields areOption.UpdateEntityInput— all non-ID fields wrapped inOption. Only provided fields are applied on update.
Parameters:
| Parameter | Type | Description |
|---|---|---|
entities | &[EntityDef] | Entity definitions from parse_schema. |
config | &DtoConfig | Output directory for generated DTO files. |
Returns: (). Writes files only.
gen_dtos(&schema.entities, &DtoConfig { output_dir: "src/schema/dto".into(),})?;gen_store
Section titled “gen_store”pub fn gen_store( entities: &[EntityDef], seaorm: Option<&SeaOrmOutput>, config: &StoreConfig,) -> Result<StoreOutput, CodegenError>Generates the CRUD store layer:
- CRUD methods —
list_*(),get_*(),create_*(),update_*(),delete_*()asimpl Storeblocks. EntityUpdatestructs — partial update types withapply()methods that patch only provided fields.Fromimplementations — conversions from DTOs to entity types and update types.- Relation population —
populate_*_relations()helpers that load junction table data after fetching. - Lifecycle hook call sites — generated CRUD methods call
hooks::before_create(),hooks::after_update(), etc. at the right points. - Hook scaffolding — creates
hooks/entity_name.rsfiles once per entity, with empty hook function bodies you fill in. These files are never overwritten.
Parameters:
| Parameter | Type | Description |
|---|---|---|
entities | &[EntityDef] | Entity definitions from parse_schema. |
seaorm | Option<&SeaOrmOutput> | When provided, uses exact table/column names. When None, falls back to naming conventions. |
config | &StoreConfig | Output directory and optional hooks directory. |
Returns: StoreOutput with metadata about generated methods, scaffolded hooks, and change channels.
let store = gen_store(&schema.entities, Some(&seaorm), &StoreConfig { output_dir: "src/store/generated".into(), hooks_dir: Some("src/store/hooks".into()), schema_module_path: ontogen::DEFAULT_SCHEMA_MODULE_PATH.into(),})?;gen_api
Section titled “gen_api”pub fn gen_api( entities: &[EntityDef], config: &ApiConfig,) -> Result<ApiOutput, CodegenError>Generates the API forwarding layer and scans for hand-written endpoints:
- Generated CRUD modules — per-entity modules with
list(),get_by_id(),create(),update(),delete()functions that forward to store methods. - Scanned custom modules — parses hand-written
.rsfiles inscan_dirsusingsyn, extracting function signatures and doc comments. - Unified API surface — merges generated and scanned functions into a single
ApiOutput. Downstream generators see no difference between generated and custom endpoints.
Parameters:
| Parameter | Type | Description |
|---|---|---|
entities | &[EntityDef] | Entity definitions from parse_schema. |
config | &ApiConfig | Output directory, exclusions, scan directories, and state type names. |
Returns: ApiOutput with all API modules (generated + scanned). Each module contains function metadata with signatures, doc comments, source origin, and classified operation kind.
let api = gen_api(&schema.entities, &ApiConfig { output_dir: "src/api/v1/generated".into(), exclude: vec![], scan_dirs: vec!["src/api/v1".into()], state_type: "AppState".to_string(), store_type: Some("Store".to_string()), schema_module_path: ontogen::DEFAULT_SCHEMA_MODULE_PATH.into(),})?;gen_servers
Section titled “gen_servers”pub fn gen_servers( api: Option<&ApiOutput>, scan_dirs: &[PathBuf], config: &ServersConfig,) -> Result<ServersOutput, CodegenError>Generates transport-specific server handlers from the unified API surface:
- Axum HTTP handlers — route functions with
GET/POST/PUT/DELETEmethods, path extraction, JSON request/response bodies, and error mapping. Includes anentity_routes()function returning a configuredRouter. - Tauri IPC commands —
#[tauri::command]functions withStateextraction andspectatype annotations for TypeScript binding generation. - MCP tool definitions — tool name, description (from doc comments), and JSON Schema parameter definitions for the Model Context Protocol.
Each transport reads the same ApiOutput and produces handlers appropriate for its protocol. The OpKind classification drives HTTP verb selection, route structure, and parameter handling. Client-side TypeScript and admin-registry generation lives in the sibling gen_clients entry point.
Parameters:
| Parameter | Type | Description |
|---|---|---|
api | Option<&ApiOutput> | Structured API metadata. When None, falls back to scanning source files with syn. Reserved for future enrichment; currently ignored. |
scan_dirs | &[PathBuf] | Additional directories to scan. Reserved for future enrichment; currently ignored — config.api_dir is always scanned. |
config | &ServersConfig | Server transport configuration — which server generators to run, import paths, naming, route prefixes, pagination, etc. |
Returns: ServersOutput describing all generated HTTP routes, IPC commands, and MCP tools.
let servers = gen_servers(Some(&api), &[], &ServersConfig { api_dir: "src/api/v1".into(), state_type: "AppState".to_string(), service_import_path: "crate::api::v1".to_string(), types_import_path: "crate::schema".to_string(), state_import: "crate::AppState".to_string(), naming: NamingConfig::default(), generators: vec![ ServerGenerator::HttpAxum { output: "src/api/transport/http/generated.rs".into(), }, ServerGenerator::TauriIpc { output: "src/api/transport/ipc/generated.rs".into(), }, ], // ... other config fields})?;gen_clients
Section titled “gen_clients”pub fn gen_clients( api: Option<&ApiOutput>, scan_dirs: &[PathBuf], config: &ClientsConfig,) -> Result<(), CodegenError>Sibling of gen_servers for the client-side surface. Reads the same ApiOutput and emits:
- TypeScript bindings — schema-known entity / DTO aliases plus the
ontogen-tslong-tail closure, written to every distinctbindings_pathdeclared by the configured generators. - TypeScript HTTP-only client (
HttpTs) — onehttpEntity*function per API operation againstfetch. - TypeScript HTTP+IPC unified transport (
HttpTauriIpcSplit) — aTransportinterface withcreateHttpTransport()/createIpcTransport()implementations. - Admin-UI registry (
AdminRegistry) — per-entity metadata (label, plural, field types, relation hints) for a generic admin layer.
Parameters:
| Parameter | Type | Description |
|---|---|---|
api | Option<&ApiOutput> | Structured API metadata. When None, falls back to scanning source files with syn. Reserved for future enrichment; currently ignored. |
scan_dirs | &[PathBuf] | Additional directories to scan. Reserved for future enrichment; currently ignored. |
config | &ClientsConfig | Client configuration — which client generators to run, ts_skip_commands, schema_entities, pool_extra_roots, etc. The Pipeline builder forwards schema.entities automatically. |
gen_clients(Some(&api), &[], &ClientsConfig { api_dir: "src/api/v1".into(), state_type: "AppState".into(), // ... shared with ServersConfig ... generators: vec![ ClientGenerator::HttpTauriIpcSplit { output: "../src-nuxt/app/transport/generated.ts".into(), bindings_path: "../src-nuxt/app/types/bindings.ts".into(), }, ClientGenerator::AdminRegistry { output: "../src-nuxt/layers/admin/generated/admin-registry.ts".into(), }, ], ts_skip_commands: vec![], schema_entities: schema.entities.clone(), pool_extra_roots: vec![], // ... other config fields})?;Pipeline
Section titled “Pipeline”pub struct Pipeline { /* ... */ }
impl Pipeline { pub fn new(schema_dir: impl Into<PathBuf>) -> Self; pub fn schema_module_path(self, path: impl Into<String>) -> Self; pub fn seaorm(self, entity_output: impl Into<PathBuf>, conversion_output: impl Into<PathBuf>) -> Self; pub fn seaorm_skip_conversions(self, skip: Vec<String>) -> Self; pub fn markdown_io(self, output_dir: impl Into<PathBuf>) -> Self; pub fn dtos(self, output_dir: impl Into<PathBuf>) -> Self; pub fn store<P: Into<PathBuf>>(self, output_dir: impl Into<PathBuf>, hooks_dir: Option<P>) -> Self; pub fn api(self, output_dir: impl Into<PathBuf>, state_type: impl Into<String>) -> Self; pub fn api_exclude(self, exclude: Vec<String>) -> Self; pub fn api_scan_dirs(self, scan_dirs: Vec<PathBuf>) -> Self; pub fn api_store_type(self, store_type: Option<String>) -> Self; pub fn servers(self, config: ServersConfig) -> Self; pub fn servers_scan_dirs(self, scan_dirs: Vec<PathBuf>) -> Self; pub fn clients(self, config: ClientsConfig) -> Self; pub fn clients_scan_dirs(self, scan_dirs: Vec<PathBuf>) -> Self; pub fn build(self) -> Result<(), CodegenError>;}A fluent builder over the generator functions above. Method order on the builder is irrelevant — build() always runs stages in dependency order (schema → seaorm/markdown_io/dtos → store → api → servers → clients) and threads each stage’s typed output into the next.
Sensible defaults are applied automatically:
schema_module_pathdefaults toDEFAULT_SCHEMA_MODULE_PATH("crate::schema") and propagates to bothStoreConfigandApiConfig.- When the
storestage is enabled and the API stage hasn’t overridden it,store_typedefaults toSome("Store"). - When the
clientsstage is enabled, the builder forwardsschema.entitiesintoClientsConfig.schema_entitiesso the admin-registry generator gets the field metadata it needs (without it, admin-registry.ts ships with emptyfields: []per entity).
ontogen::Pipeline::new("src/schema") .seaorm("src/persistence/db/entities/generated", "src/persistence/db/conversions/generated") .store("src/store/generated", Some::<std::path::PathBuf>("src/store/hooks".into())) .api("src/api/v1/generated", "AppState") .build() .expect("ontogen pipeline failed");DEFAULT_SCHEMA_MODULE_PATH
Section titled “DEFAULT_SCHEMA_MODULE_PATH”pub const DEFAULT_SCHEMA_MODULE_PATH: &str = "crate::schema";Canonical default for the schema_module_path field on StoreConfig and ApiConfig. Use this constant instead of hard-coding "crate::schema" so future changes to the convention propagate to every direct caller. Pipeline callers don’t need to reference it — the builder applies it automatically.
install_admin_layer
Section titled “install_admin_layer”pub fn install_admin_layer(config: &AdminLayerConfig) -> Result<(), CodegenError>A utility function (not a generator) that patches a Nuxt nuxt.config.ts to include the Ontogen admin layer. Idempotent — safe to call on every build.
Parameters:
| Parameter | Type | Description |
|---|---|---|
config | &AdminLayerConfig | Path to nuxt.config.ts and the relative path to the admin layer package. |
install_admin_layer(&AdminLayerConfig { nuxt_config: "../src-nuxt/nuxt.config.ts".into(), layer_path: "../crates/ontogen/packages/nuxt_admin_layer".to_string(),})?;Error handling
Section titled “Error handling”All generators return Result<T, CodegenError>. In build.rs, you typically handle errors with a helper that emits a cargo:warning before panicking:
fn unwrap_codegen<T>(result: Result<T, CodegenError>, stage: &str) -> T { result.unwrap_or_else(|e| { e.emit_cargo_warning(); panic!("{stage}: {e}"); })}CodegenError::emit_cargo_warning() prints the error as a cargo:warning=ontogen: line so it appears clearly in your build output rather than buried in a backtrace.