better_auth_entity_codegen/
attributes.rs1use super::*;
2#[derive(Clone, Debug)]
4pub struct AuthAttributes {
5 pub role: EntityRole,
6 pub secondary_storage: bool,
7 pub table: Option<String>,
8 pub id_generator: Option<syn::Path>,
10}
11
12pub fn parse_auth_attributes(
19 input: &DeriveInput,
20 allow_table: bool,
21) -> syn::Result<AuthAttributes> {
22 let mut parsed = None;
23 let mut secondary = false;
24 let mut table = None;
25 let mut id_generator = None;
26 for attr in &input.attrs {
27 if !attr.path().is_ident("auth") {
28 continue;
29 }
30 attr.parse_nested_meta(|meta| {
31 if meta.path.is_ident("role") {
32 let value = meta.value()?;
33 let role: LitStr = value.parse()?;
34 parsed = Some(match role.value().as_str() {
35 "user" => EntityRole::User,
36 "session" => EntityRole::Session,
37 "account" => EntityRole::Account,
38 "verification" => EntityRole::Verification,
39 _ => {
40 return Err(syn::Error::new_spanned(
41 role,
42 "unsupported auth role; expected user, session, account, or verification",
43 ));
44 }
45 });
46 Ok(())
47 } else if meta.path.is_ident("id_generator") {
48 let path = meta.value()?.parse::<LitStr>()?;
49 id_generator = Some(path.parse()?);
50 Ok(())
51 } else if meta.path.is_ident("secondary_storage") {
52 secondary = true;
53 Ok(())
54 } else if allow_table && meta.path.is_ident("table") {
55 table = Some(meta.value()?.parse::<LitStr>()?.value());
56 Ok(())
57 } else if allow_table {
58 Err(meta.error(
59 "expected `role = \"...\"`, `table = \"...\"`, `id_generator = \"...\"` or `secondary_storage`",
60 ))
61 } else {
62 Err(meta.error("expected `role = \"...\"` or `secondary_storage`"))
63 }
64 })?;
65 }
66
67 parsed
68 .map(|role| AuthAttributes {
69 role,
70 secondary_storage: secondary,
71 table,
72 id_generator,
73 })
74 .ok_or_else(|| {
75 syn::Error::new_spanned(
76 input,
77 "missing #[auth(role = \"...\")] attribute for AuthEntity",
78 )
79 })
80}
81
82pub fn named_fields(input: &DeriveInput) -> syn::Result<&FieldsNamed> {
88 match &input.data {
89 Data::Struct(data) => match &data.fields {
90 Fields::Named(fields) => Ok(fields),
91 Fields::Unnamed(_) | Fields::Unit => Err(syn::Error::new_spanned(
92 &input.ident,
93 "AuthEntity requires a struct with named fields",
94 )),
95 },
96 Data::Enum(_) | Data::Union(_) => Err(syn::Error::new_spanned(
97 &input.ident,
98 "AuthEntity requires a struct",
99 )),
100 }
101}
102
103pub fn validate_core_fields(
109 input: &DeriveInput,
110 role: EntityRole,
111 fields: &FieldsNamed,
112) -> syn::Result<()> {
113 if let Some(missing) = registry::core_field_names(role)
114 .iter()
115 .find(|required| !has_field(fields, required))
116 {
117 return Err(syn::Error::new_spanned(
118 &input.ident,
119 format!("missing required auth field `{missing}` for this role"),
120 ));
121 }
122 Ok(())
123}