1use super::*;
2#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
4#[serde(try_from = "UserViewInput")]
5pub struct UserView {
6 pub id: String,
7 pub name: Option<String>,
8 pub email: Option<String>,
9 #[serde(rename = "emailVerified")]
10 pub email_verified: bool,
11 pub image: Option<String>,
12 #[serde(rename = "createdAt")]
13 #[serde(serialize_with = "crate::utils::datetime::serialize")]
14 pub created_at: DateTime<Utc>,
15 #[serde(rename = "updatedAt")]
16 #[serde(serialize_with = "crate::utils::datetime::serialize")]
17 pub updated_at: DateTime<Utc>,
18 #[serde(default, skip_serializing_if = "Option::is_none")]
19 pub username: Option<String>,
20 #[serde(
21 rename = "displayUsername",
22 default,
23 skip_serializing_if = "Option::is_none"
24 )]
25 pub display_username: Option<String>,
26 #[serde(
27 rename = "twoFactorEnabled",
28 default,
29 skip_serializing_if = "Option::is_none"
30 )]
31 pub two_factor_enabled: Option<bool>,
32 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub role: Option<String>,
34 #[serde(default, skip_serializing_if = "Option::is_none")]
35 pub banned: Option<bool>,
36 #[serde(rename = "banReason", default, skip_serializing_if = "Option::is_none")]
37 pub ban_reason: Option<String>,
38 #[serde(
39 rename = "banExpires",
40 default,
41 skip_serializing_if = "Option::is_none"
42 )]
43 #[serde(serialize_with = "crate::utils::datetime::serialize_optional")]
44 pub ban_expires: Option<DateTime<Utc>>,
45 #[serde(
46 rename = "isAnonymous",
47 default,
48 skip_serializing_if = "Option::is_none"
49 )]
50 pub is_anonymous: Option<bool>,
51 #[serde(
52 rename = "phoneNumber",
53 default,
54 skip_serializing_if = "Option::is_none"
55 )]
56 pub phone_number: Option<String>,
57 #[serde(
58 rename = "phoneNumberVerified",
59 default,
60 skip_serializing_if = "Option::is_none"
61 )]
62 pub phone_number_verified: Option<bool>,
63 #[serde(
64 rename = "lastLoginMethod",
65 default,
66 skip_serializing_if = "Option::is_none"
67 )]
68 pub last_login_method: Option<String>,
69 #[serde(
71 flatten,
72 default,
73 deserialize_with = "crate::utils::json::deserialize_btree_map"
74 )]
75 pub extension_fields: std::collections::BTreeMap<String, serde_json::Value>,
76 #[serde(skip)]
77 pub metadata: serde_json::Value,
78 #[serde(skip)]
80 pub omitted_fields: std::collections::BTreeSet<String>,
81}
82
83impl Serialize for UserView {
84 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
85 use serde::ser::SerializeMap;
86 let mut map = serializer.serialize_map(None)?;
87 macro_rules! entry {
88 ($name:literal, $value:expr) => {
89 if !self.omitted_fields.contains($name) {
90 map.serialize_entry($name, $value)?;
91 }
92 };
93 }
94 entry!("id", &self.id);
95 entry!("name", &self.name);
96 entry!("email", &self.email);
97 if let Some(value) = self.extension_fields.get("emailVerified") {
98 entry!("emailVerified", value);
99 } else {
100 entry!("emailVerified", &self.email_verified);
101 }
102 entry!("image", &self.image);
103 entry!(
104 "createdAt",
105 &crate::utils::datetime::json_date_millis(self.created_at.timestamp_millis())
106 );
107 entry!(
108 "updatedAt",
109 &crate::utils::datetime::json_date_millis(self.updated_at.timestamp_millis())
110 );
111 if let Some(value) = &self.username {
112 entry!("username", value);
113 }
114 if let Some(value) = &self.display_username {
115 entry!("displayUsername", value);
116 }
117 if let Some(value) = &self.two_factor_enabled {
118 entry!("twoFactorEnabled", value);
119 }
120 if let Some(value) = &self.role {
121 entry!("role", value);
122 }
123 if let Some(value) = &self.banned {
124 entry!("banned", value);
125 }
126 if let Some(value) = &self.ban_reason {
127 entry!("banReason", value);
128 }
129 if let Some(value) = &self.ban_expires {
130 entry!(
131 "banExpires",
132 &crate::utils::datetime::json_date_millis(value.timestamp_millis())
133 );
134 }
135 if let Some(value) = &self.is_anonymous {
136 entry!("isAnonymous", value);
137 }
138 if let Some(value) = &self.phone_number {
139 entry!("phoneNumber", value);
140 }
141 if let Some(value) = &self.phone_number_verified {
142 entry!("phoneNumberVerified", value);
143 }
144 if let Some(value) = &self.last_login_method {
145 entry!("lastLoginMethod", value);
146 }
147 for (name, value) in &self.extension_fields {
148 if name != "emailVerified" && !self.omitted_fields.contains(name) {
149 map.serialize_entry(name, value)?;
150 }
151 }
152 map.end()
153 }
154}
155
156#[derive(Deserialize)]
157pub(in crate::wire) struct UserViewInput {
158 pub(in crate::wire) id: String,
159 pub(in crate::wire) name: Option<String>,
160 pub(in crate::wire) email: Option<String>,
161 #[serde(rename = "emailVerified")]
162 pub(in crate::wire) email_verified: serde_json::Value,
163 pub(in crate::wire) image: Option<String>,
164 #[serde(rename = "createdAt")]
165 #[serde(serialize_with = "crate::utils::datetime::serialize")]
166 pub(in crate::wire) created_at: DateTime<Utc>,
167 #[serde(rename = "updatedAt")]
168 #[serde(serialize_with = "crate::utils::datetime::serialize")]
169 pub(in crate::wire) updated_at: DateTime<Utc>,
170 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub(in crate::wire) username: Option<String>,
172 #[serde(
173 rename = "displayUsername",
174 default,
175 skip_serializing_if = "Option::is_none"
176 )]
177 pub(in crate::wire) display_username: Option<String>,
178 #[serde(
179 rename = "twoFactorEnabled",
180 default,
181 skip_serializing_if = "Option::is_none"
182 )]
183 pub(in crate::wire) two_factor_enabled: Option<bool>,
184 #[serde(default, skip_serializing_if = "Option::is_none")]
185 pub(in crate::wire) role: Option<String>,
186 #[serde(default, skip_serializing_if = "Option::is_none")]
187 pub(in crate::wire) banned: Option<bool>,
188 #[serde(rename = "banReason", default, skip_serializing_if = "Option::is_none")]
189 pub(in crate::wire) ban_reason: Option<String>,
190 #[serde(
191 rename = "banExpires",
192 default,
193 skip_serializing_if = "Option::is_none"
194 )]
195 #[serde(serialize_with = "crate::utils::datetime::serialize_optional")]
196 pub(in crate::wire) ban_expires: Option<DateTime<Utc>>,
197 #[serde(
198 rename = "isAnonymous",
199 default,
200 skip_serializing_if = "Option::is_none"
201 )]
202 pub(in crate::wire) is_anonymous: Option<bool>,
203 #[serde(
204 rename = "phoneNumber",
205 default,
206 skip_serializing_if = "Option::is_none"
207 )]
208 pub(in crate::wire) phone_number: Option<String>,
209 #[serde(
210 rename = "phoneNumberVerified",
211 default,
212 skip_serializing_if = "Option::is_none"
213 )]
214 pub(in crate::wire) phone_number_verified: Option<bool>,
215 #[serde(
216 rename = "lastLoginMethod",
217 default,
218 skip_serializing_if = "Option::is_none"
219 )]
220 pub(in crate::wire) last_login_method: Option<String>,
221 #[serde(
223 flatten,
224 default,
225 deserialize_with = "crate::utils::json::deserialize_btree_map"
226 )]
227 pub(in crate::wire) extension_fields: std::collections::BTreeMap<String, serde_json::Value>,
228 #[serde(skip)]
229 pub(in crate::wire) metadata: serde_json::Value,
230}
231
232impl TryFrom<UserViewInput> for UserView {
233 type Error = &'static str;
234 fn try_from(mut input: UserViewInput) -> Result<Self, Self::Error> {
235 let email_verified = match &input.email_verified {
236 serde_json::Value::Bool(value) => *value,
237 serde_json::Value::String(value) => !value.is_empty(),
238 _ => {
239 return Err("User verification output must be a boolean or retained SQLite string");
240 }
241 };
242 if !input.email_verified.is_boolean() {
243 drop(
244 input
245 .extension_fields
246 .insert("emailVerified".into(), input.email_verified),
247 );
248 }
249 Ok(Self {
250 email_verified,
251 extension_fields: input.extension_fields,
252 id: input.id,
253 name: input.name,
254 email: input.email,
255 image: input.image,
256 created_at: input.created_at,
257 updated_at: input.updated_at,
258 username: input.username,
259 display_username: input.display_username,
260 two_factor_enabled: input.two_factor_enabled,
261 role: input.role,
262 banned: input.banned,
263 ban_reason: input.ban_reason,
264 ban_expires: input.ban_expires,
265 is_anonymous: input.is_anonymous,
266 phone_number: input.phone_number,
267 phone_number_verified: input.phone_number_verified,
268 last_login_method: input.last_login_method,
269 metadata: input.metadata,
270 omitted_fields: std::collections::BTreeSet::default(),
271 })
272 }
273}
274
275impl<T: AuthUser> From<&T> for UserView {
276 fn from(user: &T) -> Self {
277 let mut view = Self {
278 id: user.id().into_owned(),
279 name: user.name().map(str::to_owned),
280 email: user.email().map(str::to_owned),
281 email_verified: user.email_verified(),
282 image: user.image().map(str::to_owned),
283 created_at: user.created_at(),
284 updated_at: user.updated_at(),
285 username: user.username().map(str::to_owned),
286 display_username: user.display_username().map(str::to_owned),
287 two_factor_enabled: user.two_factor_enabled_value(),
288 role: user.role().map(str::to_owned),
289 banned: user.banned_value(),
290 ban_reason: user.ban_reason().map(str::to_owned),
291 ban_expires: user.ban_expires(),
292 is_anonymous: user.is_anonymous(),
293 phone_number: user.phone_number().map(str::to_owned),
294 phone_number_verified: user.phone_number_verified(),
295 last_login_method: user.last_login_method().map(str::to_owned),
296 extension_fields: std::collections::BTreeMap::default(),
297 metadata: user.metadata().clone(),
298 omitted_fields: std::collections::BTreeSet::default(),
299 };
300 if let Some(value) = user
301 .adapter_snapshot()
302 .and_then(|output| output.values().get("emailVerified"))
303 && !value.is_boolean()
304 {
305 drop(
306 view.extension_fields
307 .insert("emailVerified".into(), value.clone()),
308 );
309 }
310 view
311 }
312}
313
314impl AuthUser for UserView {
315 fn retained_user_view(&self) -> Option<&UserView> {
316 Some(self)
317 }
318 fn additional_fields(&self) -> crate::field_policy::FieldOutput {
319 self.extension_fields.clone().into_iter().collect()
320 }
321 fn id(&self) -> Cow<'_, str> {
322 Cow::Borrowed(&self.id)
323 }
324 fn email(&self) -> Option<&str> {
325 self.email.as_deref()
326 }
327 fn name(&self) -> Option<&str> {
328 self.name.as_deref()
329 }
330 fn email_verified(&self) -> bool {
331 self.email_verified
332 }
333 fn image(&self) -> Option<&str> {
334 self.image.as_deref()
335 }
336 fn created_at(&self) -> DateTime<Utc> {
337 self.created_at
338 }
339 fn updated_at(&self) -> DateTime<Utc> {
340 self.updated_at
341 }
342 fn username(&self) -> Option<&str> {
343 self.username.as_deref()
344 }
345 fn display_username(&self) -> Option<&str> {
346 self.display_username.as_deref()
347 }
348 fn two_factor_enabled(&self) -> bool {
349 self.two_factor_enabled.unwrap_or(false)
350 }
351
352 fn two_factor_enabled_value(&self) -> Option<bool> {
353 self.two_factor_enabled
354 }
355 fn role(&self) -> Option<&str> {
356 self.role.as_deref()
357 }
358 fn banned(&self) -> bool {
359 self.banned.unwrap_or(false)
360 }
361
362 fn banned_value(&self) -> Option<bool> {
363 self.banned
364 }
365 fn ban_reason(&self) -> Option<&str> {
366 self.ban_reason.as_deref()
367 }
368 fn ban_expires(&self) -> Option<DateTime<Utc>> {
369 self.ban_expires
370 }
371 fn is_anonymous(&self) -> Option<bool> {
372 self.is_anonymous
373 }
374 fn phone_number(&self) -> Option<&str> {
375 self.phone_number.as_deref()
376 }
377 fn phone_number_verified(&self) -> Option<bool> {
378 self.phone_number_verified
379 }
380 fn last_login_method(&self) -> Option<&str> {
381 self.last_login_method.as_deref()
382 }
383 fn metadata(&self) -> &serde_json::Value {
384 &self.metadata
385 }
386}