better_auth_entity_codegen/
fields.rs1use super::*;
2#[must_use]
3pub fn has_field(fields: &FieldsNamed, name: &str) -> bool {
4 fields
5 .named
6 .iter()
7 .any(|field| field.ident.as_ref().is_some_and(|ident| ident == name))
8}
9
10#[must_use]
11pub fn is_option(ty: &Type) -> bool {
12 matches!(ty, Type::Path(path) if path.path.segments.last().is_some_and(|segment| segment.ident == "Option"))
13}
14
15#[must_use]
17pub fn optional_field(fields: &FieldsNamed, name: &str) -> bool {
18 fields.named.iter().any(|field| {
19 field.ident.as_ref().is_some_and(|ident| ident == name) && is_option(&field.ty)
20 })
21}
22
23#[must_use]
25pub fn is_known_field(role: EntityRole, name: &Ident) -> bool {
26 registry::core_field_names(role)
27 .iter()
28 .chain(registry::plugin_field_names(role).iter())
29 .any(|known| name == known)
30}
31
32#[must_use]
34pub fn camel_case(name: &Ident) -> String {
35 let identifier = name.to_string();
36 let text = identifier.trim_start_matches("r#");
37 let mut components = text.split('_');
38 let mut camel = components.next().unwrap_or_default().to_owned();
39 for component in components {
40 let mut letters = component.chars();
41 if let Some(first) = letters.next() {
42 camel.extend(first.to_uppercase());
43 }
44 camel.extend(letters);
45 }
46 camel
47}
48
49#[derive(Clone)]
51pub struct EntityField {
52 pub ident: Ident,
53 pub camel: String,
54 pub physical: Option<String>,
56}
57
58#[must_use]
62pub fn additional_output(
63 role: EntityRole,
64 fields: &[EntityField],
65 core_root: &TokenStream,
66) -> TokenStream {
67 let mut output = Vec::new();
68 for field in fields {
69 let name = &field.ident;
70 let camel = &field.camel;
71 let physical_output = !is_known_field(role, name)
75 || matches!(role, EntityRole::User)
76 && (name == "username" || name == "display_username");
77 if physical_output {
78 output.push(quote! {
79 if let Ok(value) = #core_root::utils::json::to_value(&self.#name) {
80 let _ = fields.insert(#camel.into(), value);
81 }
82 });
83 }
84 if let Some(physical) = field
85 .physical
86 .as_ref()
87 .filter(|physical| *physical != camel)
88 && physical_output
89 {
90 output.push(quote! {
91 if let Ok(value) = #core_root::utils::json::to_value(&self.#name) {
92 let _ = fields.insert(#physical.into(), value);
93 }
94 });
95 }
96 }
97 quote! {
98 fn additional_fields(&self) -> #core_root::field_policy::FieldOutput {
99 let mut fields = #core_root::field_policy::FieldOutput::new();
100 #(#output)*
101 fields
102 }
103 }
104}
105
106#[must_use]
108pub fn is_auth_timestamp(name: &str) -> bool {
109 matches!(
110 name,
111 "created_at"
112 | "updated_at"
113 | "expires_at"
114 | "ban_expires"
115 | "access_token_expires_at"
116 | "refresh_token_expires_at"
117 )
118}