Skip to main content

alibi_seaorm_macros/
lib.rs

1//! Proc macros for the Alibi `SeaORM` integration.
2mod entities;
3mod fields;
4mod roots;
5
6use alibi_entity_codegen::{self as codegen, EntityRole};
7use entities::{account_impl, session_impl, user_impl, verification_impl};
8use fields::model_fields;
9use proc_macro::TokenStream as ProcMacroTokenStream;
10use proc_macro2::TokenStream;
11use quote::quote;
12use roots::resolve_roots;
13use syn::{DeriveInput, parse_macro_input};
14
15fn try_generate(input: &DeriveInput) -> syn::Result<TokenStream> {
16    let mut roots = resolve_roots();
17    let attributes = codegen::parse_auth_attributes(input, false)?;
18    roots.id_generator = codegen::generated_id(&attributes, &roots.core);
19    let fields = codegen::named_fields(input)?;
20    codegen::validate_core_fields(input, attributes.role, fields)?;
21    let model_fields = model_fields(fields)?;
22    let entity_fields: Vec<codegen::EntityField> = model_fields
23        .iter()
24        .map(|field| codegen::EntityField {
25            ident: field.ident.clone(),
26            camel: field.camel.clone(),
27            physical: field.physical.clone(),
28        })
29        .collect();
30    let ident = &input.ident;
31    let secondary = attributes.secondary_storage;
32    let core_root = &roots.core;
33    Ok(match attributes.role {
34        EntityRole::User => {
35            let auth = codegen::auth_user_impl(ident, fields, &entity_fields, secondary, core_root);
36            let model = user_impl(ident, fields, &model_fields, &roots);
37            quote! { #auth #model }
38        }
39        EntityRole::Session => {
40            let auth =
41                codegen::auth_session_impl(ident, fields, &entity_fields, secondary, core_root);
42            let model = session_impl(ident, fields, &model_fields, &roots);
43            quote! { #auth #model }
44        }
45        EntityRole::Account => {
46            let auth = codegen::auth_account_impl(ident, &entity_fields, core_root);
47            let model = account_impl(ident, fields, &model_fields, &roots);
48            quote! { #auth #model }
49        }
50        EntityRole::Verification => {
51            let auth = codegen::auth_verification_impl(ident, core_root);
52            let model = verification_impl(ident, fields, &roots);
53            quote! { #auth #model }
54        }
55    })
56}
57
58/// Derive macro that generates `Auth*` trait impls and `SeaOrm*Model` impls
59/// for a `SeaORM` entity.
60///
61/// # Usage
62///
63/// Annotate a `SeaORM` `Model` struct with `#[derive(AuthEntity)]` and
64/// `#[auth(role = "...")]` where role is one of `user`, `session`, `account`,
65/// or `verification`. `id_generator = "path::to::function"` selects a String ID
66/// factory; the default remains a UUID.
67///
68/// ```ignore
69/// #[derive(DeriveEntityModel, AuthEntity)]
70/// #[auth(role = "user")]
71/// #[sea_orm(table_name = "users")]
72/// pub struct Model {
73///     #[sea_orm(primary_key, auto_increment = false)]
74///     pub id: String,
75///     // ... core fields ...
76/// }
77/// ```
78///
79/// # Extra fields
80///
81/// The struct may contain fields beyond the core set required by the auth
82/// role. They are set to `ActiveValue::NotSet` in the generated
83/// `new_active()`, so use database defaults or
84/// `ActiveModelBehavior::before_save` to populate them.
85#[proc_macro_derive(AuthEntity, attributes(auth))]
86pub fn derive_auth_entity(input: ProcMacroTokenStream) -> ProcMacroTokenStream {
87    let input = parse_macro_input!(input as DeriveInput);
88    try_generate(&input)
89        .unwrap_or_else(syn::Error::into_compile_error)
90        .into()
91}