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