Skip to content

Field Types

When parse_schema() reads your annotated structs, it maps each field’s Rust type into a FieldType variant and each #[ontology(...)] annotation into a FieldRole. These two enums drive all downstream code generation — how columns are typed in SeaORM, how fields appear in DTOs, what TypeScript type is emitted, etc.

A simplified representation of the Rust type. Ontogen only needs to know the shape of a type for code generation — it doesn’t need to resolve the full type system.

FieldType variantRust typeSeaORM column typeSQL type (SQLite)TypeScript type
StringStringStringTEXT NOT NULLstring
OptionStringOption<String>Option<String>TEXTstring | null
I32i32i32INTEGER NOT NULLnumber
OptionI32Option<i32>Option<i32>INTEGERnumber | null
I64i64i64INTEGER NOT NULLnumber
OptionI64Option<i64>Option<i64>INTEGERnumber | null
F32f32f32REAL NOT NULLnumber
OptionF32Option<f32>Option<f32>REALnumber | null
F64f64f64REAL NOT NULLnumber
OptionF64Option<f64>Option<f64>REALnumber | null
BoolboolboolBOOLEAN NOT NULLboolean
OptionBoolOption<bool>Option<bool>BOOLEANboolean | null
OptionEnum(name)Option<EnumType>Option<String>TEXTstring | null
VecStringVec<String>String (JSON)TEXT NOT NULLstring[]
VecStruct(name)Vec<StructType>String (JSON)TEXT NOT NULLStructType[]
Other(name)anything elseStringTEXT NOT NULLstring

parse_schema() uses syn to inspect the Type of each field. The detection logic is straightforward pattern matching:

  • String — exact match.
  • Option<String>Option wrapper with String inner type.
  • Option<SomeType> where the field has #[ontology(enum_field)] — becomes OptionEnum("SomeType").
  • Vec<String>Vec wrapper with String inner type.
  • Vec<SomeType> where SomeType is not String — becomes VecStruct("SomeType").
  • i32, i64, f32, f64, bool and their Option wrappers — exact matches.
  • Everything else — Other("TypeName"), treated as a string-serialized value.

Vec<String> and Vec<StructType> fields are stored as JSON-serialized strings in the database column. The SeaORM entity uses a String column, and the conversion layer handles serde_json::to_string() / serde_json::from_str().

The exception is Vec<String> fields annotated with a relation (belongs_to, has_many, many_to_many). These aren’t stored in the entity’s table at all — they’re populated at read time from the related table or junction table.

When a field has #[ontology(enum_field)] and an Option<T> type, the FieldType is OptionEnum("T"). The string inside is the enum type name.

In the SeaORM entity, this becomes an Option<String> column. The conversion code uses .map(|v| v.to_string()) going into the database and .map(|s| s.parse().unwrap()) coming out.

Your enum type needs to implement Display + FromStr or handle serde string conversion. Ontogen doesn’t generate the enum — you provide it.

Any type that doesn’t match the known patterns becomes Other("TypeName"). It’s treated as string-serialized in the database. This works for custom types that implement Serialize/Deserialize, but you lose type-specific behavior in the generated code.


Determines how a field participates in code generation. Set by #[ontology(...)] annotations on the field.

FieldRole variantAnnotationDescription
Id#[ontology(id)]Primary key. One per entity.
Body#[ontology(body)]Markdown body content (below frontmatter).
EnumField#[ontology(enum_field)]Enum type stored as string.
Relation(RelationInfo)#[ontology(relation(...))]Cross-entity reference.
Plain(none)Regular data field. Default for unannotated fields.
Skip#[ontology(skip)]Excluded from all code generation.
GeneratorIdBodyEnumFieldRelationPlainSkip
SeaORM entityPrimary keyRegular columnString columnFK column or excludedRegular columnExcluded
SeaORM conversionIncludedIncludedString conversionFK: included; Vec: skipped (populated separately)IncludedExcluded
DTOs (Create)IncludedIncludedIncludedIncludedIncludedExcluded
DTOs (Update)ExcludedIncludedIncludedIncludedIncludedExcluded
Store CRUDLookup keyRegular fieldRegular fieldRelation population / junction syncRegular fieldExcluded
Markdown writerFrontmatterBody sectionFrontmatterWikilink formattingFrontmatterExcluded
Markdown parserFrontmatterBody sectionFrontmatterWikilink parsingFrontmatterExcluded

Attached to fields with FieldRole::Relation. Describes the relationship between entities.

FieldTypeDescription
kindRelationKindThe cardinality of the relation.
targetStringTarget entity name (e.g., "Tag", "Workout"). Must match a struct name in your schema.
junctionOption<String>Override for junction table name. Only used with ManyToMany. Derived automatically if not specified.
foreign_keyOption<String>FK column on the target entity. Only used with HasMany.
VariantSchema field typeJunction tableFK columnDescription
BelongsToString or Option<String>NoOn this entityThis entity holds a FK pointing to the target.
HasManyVec<String>NoOn the target entityThe target entity has a FK pointing back to this entity.
ManyToManyVec<String>YesIn the junction tableBoth sides linked through a junction table.
#[ontology(relation(belongs_to, target = "Workout"))]
pub workout_id: String,

The workout_id column exists on this entity’s table. SeaORM generates a BelongsTo relation variant.

#[ontology(relation(has_many, target = "WorkoutSet", foreign_key = "workout_id"))]
pub sets: Vec<String>,

No column on this entity’s table. The WorkoutSet entity has a workout_id column. At read time, populate_*_relations() queries workout_sets where workout_id matches.

#[ontology(relation(many_to_many, target = "Tag"))]
pub tags: Vec<String>,

A junction table (workout_tags) is generated with workout_id and tag_id columns. At read time, populate_*_relations() loads tag IDs from the junction table. On create/update, sync_junction() replaces the junction rows.


The full field definition as parsed from your schema struct.

FieldTypeDescription
nameStringRust field name (e.g., "workout_id", "tags").
field_typeFieldTypeSimplified type representation.
roleFieldRoleRole in the ontology model.
serde_defaultboolWhether the field has #[serde(default)].
multiline_listboolWhether #[ontology(multiline_list)] is present. Affects Markdown rendering.
default_valueOption<String>Value from #[ontology(default_value = "...")]. When the field matches this value, it’s omitted from Markdown frontmatter.

The full entity definition. One per annotated struct.

FieldTypeDefaultDescription
nameStringRust struct name (e.g., "Workout").
directoryStringsnake_case of nameSubdirectory for Markdown files.
tableStringsnake_case of nameSeaORM table name.
type_nameStringsnake_case of nameFrontmatter type: value.
prefixStringsnake_case of nameID prefix.
fieldsVec<FieldDef>All fields on the struct.
MethodReturn typeDescription
id_field()Option<&FieldDef>The field with FieldRole::Id, if any.
body_field()Option<&FieldDef>The field with FieldRole::Body, if any.
relation_fields()impl Iterator<Item = &FieldDef>All fields with FieldRole::Relation(_).
junction_relations()impl Iterator<Item = (&FieldDef, &RelationInfo)>All ManyToMany relation fields.
has_many_relations()impl Iterator<Item = (&FieldDef, &RelationInfo)>All HasMany relation fields.
belongs_to_relations()impl Iterator<Item = (&FieldDef, &RelationInfo)>All BelongsTo relation fields.