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