modo_db_macros/lib.rs
1use proc_macro::TokenStream;
2
3mod entity;
4mod migration;
5
6/// Attribute macro for declaring a SeaORM database entity with auto-registration.
7///
8/// The macro wraps the annotated struct in a SeaORM entity module and submits an
9/// `EntityRegistration` to the `inventory` collector so `modo_db::sync_and_migrate`
10/// can discover it at startup.
11///
12/// # Required argument
13///
14/// - `table = "<name>"` — SQL table name.
15///
16/// # Optional argument
17///
18/// - `group = "<name>"` — assigns the entity to a named group (default: `"default"`).
19/// Entities in a group can be synced separately via `modo_db::sync_and_migrate_group`.
20///
21/// # Struct-level options (applied as a second `#[entity(...)]` attribute)
22///
23/// - `timestamps` — injects `created_at` and `updated_at` columns of type
24/// `DateTime<Utc>` and sets them automatically in `before_save`.
25/// - `soft_delete` — injects a `deleted_at: Option<DateTime<Utc>>` column and
26/// generates `find`, `find_by_id`, `with_deleted`, `only_deleted`, `soft_delete`,
27/// `restore`, and `force_delete` helpers on the entity module.
28/// - `framework` — marks the entity as framework-internal (non-user schema).
29/// - `index(columns = ["col1", "col2"])` — creates a composite index. Add `unique`
30/// inside to make it a unique index.
31///
32/// # Field-level options (applied as `#[entity(...)]` on individual fields)
33///
34/// - `primary_key` — marks the field as the primary key.
35/// - `auto_increment = true|false` — overrides SeaORM's default auto-increment behaviour.
36/// - `auto = "ulid"|"nanoid"` — generates a ULID or NanoID before insert; only valid
37/// on `primary_key` fields.
38/// - `unique` — adds a unique constraint.
39/// - `indexed` — creates a single-column index.
40/// - `nullable` — accepted but has no effect (SeaORM infers nullability from `Option<T>`).
41/// - `column_type = "<type>"` — overrides the inferred SeaORM column type string.
42/// - `default_value = <literal>` — sets a default value (passed to SeaORM).
43/// - `default_expr = "<expr>"` — sets a default SQL expression string.
44/// - `belongs_to = "<Entity>"` — declares a `BelongsTo` relation to the named entity.
45/// Pair with `on_delete` / `on_update` as needed.
46/// - `on_delete = "<action>"` — FK action on delete. One of: `Cascade`, `SetNull`,
47/// `Restrict`, `NoAction`, `SetDefault`.
48/// - `on_update = "<action>"` — FK action on update. Same values as `on_delete`.
49/// - `has_many` — declares a `HasMany` relation (field is excluded from the model).
50/// - `has_one` — declares a `HasOne` relation (field is excluded from the model).
51/// - `via = "<JoinEntity>"` — used with `has_many` / `has_one` for many-to-many
52/// relations through a join entity.
53/// - `renamed_from = "<old_name>"` — records a rename hint as a column comment.
54///
55/// # Example
56///
57/// ```rust,ignore
58/// #[modo_db::entity(table = "users")]
59/// #[entity(timestamps, soft_delete)]
60/// pub struct User {
61/// #[entity(primary_key, auto = "ulid")]
62/// pub id: String,
63/// #[entity(unique)]
64/// pub email: String,
65/// pub name: String,
66/// }
67/// ```
68#[proc_macro_attribute]
69pub fn entity(attr: TokenStream, item: TokenStream) -> TokenStream {
70 entity::expand(attr.into(), item.into())
71 .unwrap_or_else(|e| e.to_compile_error())
72 .into()
73}
74
75/// Attribute macro for registering an escape-hatch SQL migration function.
76///
77/// The annotated async function is kept as-is and a `MigrationRegistration` is submitted
78/// to the `inventory` collector so `modo_db::sync_and_migrate` runs it in version order.
79///
80/// # Required arguments
81///
82/// - `version = <u64>` — monotonically increasing migration version number.
83/// - `description = "<text>"` — human-readable description shown in logs.
84///
85/// # Optional argument
86///
87/// - `group = "<name>"` — assigns the migration to a named group (default: `"default"`).
88/// Migrations in a group run only when `modo_db::sync_and_migrate_group` is called
89/// with the matching group name.
90///
91/// # Function signature
92///
93/// The annotated function must be `async` and accept a single `&C` parameter where
94/// `C: ConnectionTrait`. Return type must be `Result<(), DbErr>`.
95///
96/// # Example
97///
98/// ```rust,ignore
99/// #[modo_db::migration(version = 1, description = "seed default roles")]
100/// async fn seed_roles(db: &impl modo_db::sea_orm::ConnectionTrait)
101/// -> Result<(), modo_db::sea_orm::DbErr>
102/// {
103/// // run raw SQL or SeaORM operations
104/// Ok(())
105/// }
106/// ```
107#[proc_macro_attribute]
108pub fn migration(attr: TokenStream, item: TokenStream) -> TokenStream {
109 migration::expand(attr.into(), item.into())
110 .unwrap_or_else(|e| e.to_compile_error())
111 .into()
112}