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.
- TypeScript clients and admin registry — driven by
ServersConfig.client_generators. The same call emits HTTP-only clients, the HTTP-or-IPC split transport, and the admin-UI registry.
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.
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 | Transport configuration — which server generators to run, which client 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(), }, ], client_generators: vec![], // schema_entities is required for the admin-registry client generator; // pass `schema.entities.clone()` from `parse_schema`. The Pipeline // builder forwards this automatically. schema_entities: schema.entities.clone(), // ... 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 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) 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
serversstage is enabled, the builder forwardsschema.entitiesintoServersConfig.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.