1use super::*;
2#[derive(Clone)]
4pub enum Insert {
5 Value(TokenStream),
8 Null,
10 Default,
12}
13
14#[must_use]
17pub fn insert_values(role: EntityRole, fields: &FieldsNamed) -> Vec<(Ident, Insert)> {
18 let has = |name: &str| has_field(fields, name);
19 let optional = |name: &str| optional_field(fields, name);
20 let mut values: Vec<(&str, Insert)> = Vec::new();
21 let mut value =
22 |name: &'static str, expr: TokenStream| values.push((name, Insert::Value(expr)));
23 match role {
24 EntityRole::User => {
25 for name in ["email", "name"] {
26 let field = Ident::new(name, Span::call_site());
27 let expression = if optional(name) {
28 quote! { create_user.#field }
29 } else {
30 quote! { create_user.#field.unwrap_or_default() }
31 };
32 value(name, expression);
33 }
34 value("image", quote! { create_user.image });
35 value(
36 "email_verified",
37 quote! { create_user.email_verified.unwrap_or(false) },
38 );
39 value(
40 "created_at",
41 quote! { create_user.created_at.unwrap_or(now) },
42 );
43 value(
44 "updated_at",
45 quote! { create_user.updated_at.unwrap_or(now) },
46 );
47 for name in [
48 "username",
49 "display_username",
50 "role",
51 "is_anonymous",
52 "phone_number",
53 "phone_number_verified",
54 "last_login_method",
55 ] {
56 let field = Ident::new(name, Span::call_site());
57 value(name, quote! { create_user.#field });
58 }
59 for name in ["two_factor_enabled", "banned"] {
62 let field = Ident::new(name, Span::call_site());
63 if optional(name) {
64 value(name, quote! { create_user.#field });
65 } else {
66 value(name, quote! { create_user.#field.unwrap_or(false) });
67 }
68 }
69 value(
70 "metadata",
71 quote! { create_user.metadata.unwrap_or(::serde_json::json!({})) },
72 );
73 values.push(("ban_reason", Insert::Null));
74 values.push(("ban_expires", Insert::Null));
75 }
76 EntityRole::Session => {
77 value("token", quote! { token });
78 value("created_at", quote! { now });
79 value("updated_at", quote! { now });
80 value("active", quote! { true });
81 for name in [
82 "user_id",
83 "expires_at",
84 "ip_address",
85 "user_agent",
86 "impersonated_by",
87 "active_organization_id",
88 "active_team_id",
89 ] {
90 let field = Ident::new(name, Span::call_site());
91 value(name, quote! { create_session.#field });
92 }
93 }
94 EntityRole::Account => {
95 value("created_at", quote! { now });
96 value("updated_at", quote! { now });
97 for name in [
98 "account_id",
99 "provider_id",
100 "user_id",
101 "access_token",
102 "refresh_token",
103 "id_token",
104 "access_token_expires_at",
105 "refresh_token_expires_at",
106 "scope",
107 "password",
108 ] {
109 let field = Ident::new(name, Span::call_site());
110 value(name, quote! { create_account.#field });
111 }
112 }
113 EntityRole::Verification => {
114 value("created_at", quote! { now });
115 value("updated_at", quote! { now });
116 for name in ["identifier", "value", "expires_at"] {
117 let field = Ident::new(name, Span::call_site());
118 value(name, quote! { verification.#field });
119 }
120 }
121 }
122 fields
123 .named
124 .iter()
125 .filter_map(|field| field.ident.clone())
126 .filter(|ident| ident != "id")
127 .map(|ident| {
128 let insert = values
129 .iter()
130 .find(|(name, _)| ident == name && has(name))
131 .map_or(Insert::Default, |(_, insert)| insert.clone());
132 (ident, insert)
133 })
134 .collect()
135}
136
137pub type SetField<'a> = &'a dyn Fn(&Ident, &Insert) -> TokenStream;
141
142#[must_use]
145pub fn update_statements(role: EntityRole, fields: &FieldsNamed, set: SetField<'_>) -> TokenStream {
146 let has = |name: &str| has_field(fields, name);
147 let ident = |name: &str| Ident::new(name, Span::call_site());
148 let some = |name: &str, wrap: fn(&Ident) -> TokenStream| {
150 let field = ident(name);
151 let value = wrap(&field);
152 let assign = set(&field, &Insert::Value(value));
153 quote! { if let ::std::option::Option::Some(#field) = update.#field { #assign } }
154 };
155 let wrapped = |field: &Ident| quote! { ::std::option::Option::Some(#field) };
156 let direct = |field: &Ident| quote! { #field };
157 let mut statements = Vec::new();
158 match role {
159 EntityRole::User => {
160 for name in ["email", "name", "image"] {
161 let wrap = if optional_field(fields, name) {
162 wrapped
163 } else {
164 direct
165 };
166 statements.push(some(name, wrap));
167 }
168 statements.push(some("email_verified", direct));
169 for name in ["username", "display_username", "role"] {
170 if has(name) {
171 statements.push(some(name, wrapped));
172 }
173 }
174 for name in ["two_factor_enabled", "metadata"] {
175 if has(name) {
176 statements.push(some(name, direct));
177 }
178 }
179 if has("banned") && has("ban_reason") && has("ban_expires") {
180 let banned = set(&ident("banned"), &Insert::Value(quote! { banned }));
181 let clear_reason = set(&ident("ban_reason"), &Insert::Null);
182 let clear_expires = set(&ident("ban_expires"), &Insert::Null);
183 let reason = set(
184 &ident("ban_reason"),
185 &Insert::Value(quote! { ::std::option::Option::Some(ban_reason) }),
186 );
187 let expires = set(
188 &ident("ban_expires"),
189 &Insert::Value(quote! { ban_expires }),
190 );
191 statements.push(quote! {
192 if let ::std::option::Option::Some(banned) = update.banned {
193 #banned
194 if !banned {
195 #clear_reason
196 #clear_expires
197 }
198 }
199 if update.banned != ::std::option::Option::Some(false) {
200 if let ::std::option::Option::Some(ban_reason) = update.ban_reason { #reason }
201 if let ::std::option::Option::Some(ban_expires) = update.ban_expires { #expires }
202 }
203 });
204 } else if has("banned") {
205 statements.push(some("banned", direct));
206 }
207 for name in ["is_anonymous", "phone_number_verified"] {
208 if has(name) {
209 statements.push(some(name, wrapped));
210 }
211 }
212 for name in ["phone_number", "last_login_method"] {
213 if has(name) {
214 statements.push(some(name, direct));
215 }
216 }
217 }
218 EntityRole::Account => {
219 for (index, name) in ["access_token", "refresh_token", "id_token"]
220 .into_iter()
221 .enumerate()
222 {
223 let clear = set(&ident(name), &Insert::Null);
224 statements.push(quote! { if update.provider_token_nulls[#index] { #clear } });
225 }
226 for name in [
227 "access_token",
228 "refresh_token",
229 "id_token",
230 "access_token_expires_at",
231 "refresh_token_expires_at",
232 "scope",
233 "password",
234 ] {
235 statements.push(some(name, wrapped));
236 }
237 }
238 EntityRole::Session | EntityRole::Verification => {}
239 }
240 let updated_at = set(&ident("updated_at"), &Insert::Value(quote! { now }));
241 quote! { #(#statements)* #updated_at }
242}
243
244#[must_use]
246pub fn generated_id(attributes: &AuthAttributes, core_root: &TokenStream) -> TokenStream {
247 attributes.id_generator.as_ref().map_or_else(
248 || quote! { #core_root::uuid::Uuid::new_v4().to_string() },
249 |generator| quote! { #generator() },
250 )
251}