modo-db-macros
Procedural macros powering the modo-db entity and migration system.
This crate is an implementation detail of modo-db. Consume these macros through the
modo_db re-exports (modo_db::entity and modo_db::migration) — do not add
modo-db-macros as a direct dependency.
Macros
#[modo_db::entity(table = "...", group = "...")]
Transforms an annotated struct into a fully-formed domain model backed by a SeaORM entity
module and registers it with the inventory collector so modo_db::sync_and_migrate
discovers it at startup.
The original struct is preserved as a first-class domain type. You work with the struct
directly rather than with the SeaORM Model.
The optional group parameter (defaults to "default") assigns the entity to a named group.
Entities in a group can be synced separately via modo_db::sync_and_migrate_group.
Struct-level options
Place these as a second #[entity(...)] attribute on the struct itself.
| Option | Effect |
|---|---|
timestamps |
Injects created_at and updated_at: DateTime<Utc> columns; set automatically on every insert and update. |
soft_delete |
Injects deleted_at: Option<DateTime<Utc>>. The delete method becomes a soft-delete (sets deleted_at). Extra methods generated: with_deleted, only_deleted, restore, force_delete, force_delete_by_id, delete_many (bulk soft-delete), force_delete_many (bulk hard-delete). find_all and query exclude soft-deleted rows automatically. |
framework |
Marks the entity as framework-internal (hidden from user schema). |
index(columns = ["col1", "col2"]) |
Creates a composite index. Add unique inside for a unique index. |
Field-level options
Place these as #[entity(...)] on individual struct fields.
| Option | Effect |
|---|---|
primary_key |
Marks the field as the primary key. |
auto_increment = true|false |
Overrides SeaORM's default auto-increment behaviour. |
auto = "ulid"|"short_id" |
Generates a ULID or short ID before insert. Only valid on primary_key fields. |
unique |
Adds a unique constraint. |
indexed |
Creates a single-column index. |
nullable |
Accepted but has no effect. Option<T> already implies nullable in SeaORM. |
column_type = "<type>" |
Overrides the inferred SeaORM column type string. |
default_value = <literal> |
Sets a column default value. |
default_expr = "<expr>" |
Sets a default SQL expression. |
belongs_to = "<Entity>" |
Declares a BelongsTo relation to the named entity. |
to_column = "<Column>" |
Overrides the target column for a belongs_to FK (default: "Id"). |
on_delete = "<action>" |
FK action on delete: Cascade, SetNull, Restrict, NoAction, SetDefault. |
on_update = "<action>" |
FK action on update. Same values as on_delete. |
has_many |
Declares a HasMany relation (field excluded from the model columns). Requires target = "<Entity>". |
has_one |
Declares a HasOne relation (field excluded from the model columns). |
via = "<JoinEntity>" |
Many-to-many via a join entity. Used with has_many or has_one. |
target = "<Entity>" |
Overrides the inferred target entity name for has_many / has_one when the field name does not match the entity name. Required for has_many. |
renamed_from = "<old>" |
Records a rename hint as a column comment. |
What the macro emits
For a struct named Foo, the macro emits:
- The original
Foostruct with#[derive(Clone, Debug, serde::Serialize)] impl Default for Foo— auto-generates IDs (ULID/short_id), sets timestamps toUtc::now(), uses type defaults for all other fields (String::new(),false,0,None, etc.)impl From<foo::Model> for Foo— converts a SeaORM model to the domain structpub mod foo { ... }containing:Model— the SeaORM model struct with all columnsActiveModel— SeaORM active modelEntity— SeaORM entity typeColumn— column enumRelation— relation enumActiveModelBehaviorimpl — always empty; auto-ID and timestamp logic lives inRecord::apply_auto_fieldsinstead
impl modo_db::Record for Foo— wires upEntity,ActiveModel,from_model,into_active_model_full,into_active_model, andapply_auto_fields; also overridesfind_allandqueryto exclude soft-deleted rows whensoft_deleteis set- Inherent
impl Foowith CRUD methods:insert,update,delete,find_by_id,delete_by_id - Relation accessor methods (when relations are declared): e.g.
post.user(&db),user.posts(&db) - Soft-delete methods on the struct (when
soft_deleteis set):restore,force_delete,force_delete_by_id,with_deleted,only_deleted,delete_many(bulk soft-delete),force_delete_many(bulk hard-delete) - An
inventory::submit!block that registers the entity for schema sync
Basic entity example
// Usage — struct-update syntax with generated Default:
let todo = Todo .insert.await?;
Entity with relations
// Generated accessor methods:
let author: = post.user.await?;
let posts: = user.posts.await?;
Composite index
#[modo_db::migration(version = <u64>, description = "...", group = "...")]
Registers a migration function. modo_db::sync_and_migrate runs all
registered migrations in ascending version order after schema sync.
The optional group parameter (defaults to "default") assigns the migration to a named group.
Migrations in a group run only when modo_db::sync_and_migrate_group is called with the
matching group name.
The annotated function must be async fn(db: &sea_orm::DatabaseConnection) -> Result<(), modo::Error>.
The db parameter implements ConnectionTrait, so the full SeaORM typed API is available:
async
Integration with modo-db
Register entities and migrations simply by declaring them — no manual registration call is needed. Then at startup:
let db = connect.await?;
sync_and_migrate.await?;
sync_and_migrate discovers all #[modo_db::entity] and #[modo_db::migration]
declarations via inventory and applies them.