1use crate::core::{FinitePeriod, Instant};
2use crate::dinoparc::{DinoparcPassword, DinoparcServer, DinoparcUserId, DinoparcUsername};
3use crate::email::EmailAddress;
4use crate::hammerfest::{
5 HammerfestPassword, HammerfestServer, HammerfestSessionKey, HammerfestUserId, HammerfestUsername,
6};
7use crate::link::VersionedLinks;
8use crate::oauth::RfcOauthAccessToken;
9use crate::password::{Password, PasswordHash};
10use crate::twinoid::TwinoidUserId;
11use crate::types::AnyError;
12use async_trait::async_trait;
13use auto_impl::auto_impl;
14use chrono::Duration;
15#[cfg(feature = "serde")]
16use etwin_serde_tools::{deserialize_explicit_option, deserialize_nested_option, Deserialize, Serialize};
17use once_cell::sync::Lazy;
18use std::error::Error;
19
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
22#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct CompleteSimpleUser {
24 pub id: UserId,
25 pub created_at: Instant,
26 pub display_name: UserDisplayNameVersions,
27 pub is_administrator: bool,
28 #[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_explicit_option"))]
29 pub username: Option<Username>,
30 #[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_explicit_option"))]
31 pub email_address: Option<EmailAddress>,
32}
33
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
36#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub struct CompleteUser {
38 pub id: UserId,
39 pub created_at: Instant,
40 pub display_name: UserDisplayNameVersions,
41 pub is_administrator: bool,
42 pub links: VersionedLinks,
43 pub username: Option<Username>,
44 pub email_address: Option<EmailAddress>,
45 pub has_password: bool,
46}
47
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
49#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
50pub struct CreateUserOptions {
51 pub display_name: UserDisplayName,
52 pub email: Option<EmailAddress>,
53 pub username: Option<Username>,
54 pub password: Option<PasswordHash>,
55}
56
57#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
58#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
59pub struct UpdateUserOptions {
60 pub r#ref: UserIdRef,
61 pub patch: UpdateUserPatch,
62}
63
64#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
65#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
66pub struct UpdateUserPatch {
67 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
68 pub display_name: Option<UserDisplayName>,
69 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
70 #[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_nested_option"))]
71 pub username: Option<Option<Username>>,
72 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
73 #[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_nested_option"))]
74 pub password: Option<Option<Password>>,
75}
76
77#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
78#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
79pub struct RawUpdateUserOptions {
80 pub actor: UserIdRef,
81 pub r#ref: UserIdRef,
82 pub patch: RawUpdateUserPatch,
83}
84
85#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
86#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
87pub struct RawUpdateUserPatch {
88 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
89 pub display_name: Option<UserDisplayName>,
90 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
91 #[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_nested_option"))]
92 pub username: Option<Option<Username>>,
93 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
94 #[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_nested_option"))]
95 pub password: Option<Option<PasswordHash>>,
96}
97
98#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(tag = "type"))]
99#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
100pub enum UserFields {
101 CompleteIfSelf { self_user_id: UserId },
102 Complete,
103 Default,
104 Short,
105}
106
107#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
108#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
109pub struct GetUserOptions {
110 pub r#ref: UserRef,
111 pub fields: UserFields,
112 pub time: Option<Instant>,
113}
114
115#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
116#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
117pub struct GetShortUserOptions {
118 pub r#ref: UserRef,
119 pub time: Option<Instant>,
120}
121
122impl From<UserRef> for GetShortUserOptions {
123 fn from(r#ref: UserRef) -> Self {
124 Self { r#ref, time: None }
125 }
126}
127
128impl From<UserIdRef> for GetShortUserOptions {
129 fn from(r: UserIdRef) -> Self {
130 let r: UserRef = r.into();
131 r.into()
132 }
133}
134
135impl From<UserId> for GetShortUserOptions {
136 fn from(id: UserId) -> Self {
137 let r: UserRef = id.into();
138 r.into()
139 }
140}
141
142#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
143#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
144pub enum GetUserResult {
145 Complete(CompleteUser),
146 Default(User),
147}
148
149#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
150#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
151pub enum RawGetUserResult {
152 Complete(CompleteSimpleUser),
153 Default(SimpleUser),
154 Short(ShortUser),
155}
156
157impl RawGetUserResult {
158 pub fn id(&self) -> UserId {
159 match self {
160 Self::Complete(u) => u.id,
161 Self::Default(u) => u.id,
162 Self::Short(u) => u.id,
163 }
164 }
165}
166
167#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
168#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
169#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
170pub struct ShortUser {
171 pub id: UserId,
172 pub display_name: UserDisplayNameVersions,
173}
174
175impl ShortUser {
176 pub const fn as_ref(&self) -> UserIdRef {
177 UserIdRef { id: self.id }
178 }
179}
180
181impl From<SimpleUser> for ShortUser {
182 fn from(user: SimpleUser) -> Self {
183 Self {
184 id: user.id,
185 display_name: user.display_name,
186 }
187 }
188}
189
190impl From<CompleteSimpleUser> for ShortUser {
191 fn from(user: CompleteSimpleUser) -> Self {
192 Self {
193 id: user.id,
194 display_name: user.display_name,
195 }
196 }
197}
198
199impl From<User> for ShortUser {
200 fn from(user: User) -> Self {
201 Self {
202 id: user.id,
203 display_name: user.display_name,
204 }
205 }
206}
207
208#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
209#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
210#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
211pub struct ShortUserWithPassword {
212 pub id: UserId,
213 pub display_name: UserDisplayNameVersions,
214 pub password: Option<PasswordHash>,
215}
216
217#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
218#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
219#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
220pub struct SimpleUser {
221 pub id: UserId,
222 pub created_at: Instant,
223 pub display_name: UserDisplayNameVersions,
224 pub is_administrator: bool,
225}
226
227impl From<CompleteSimpleUser> for SimpleUser {
228 fn from(user: CompleteSimpleUser) -> Self {
229 Self {
230 id: user.id,
231 created_at: user.created_at,
232 display_name: user.display_name,
233 is_administrator: user.is_administrator,
234 }
235 }
236}
237
238#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
239#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
240#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
241pub struct User {
242 pub id: UserId,
243 pub created_at: Instant,
244 pub display_name: UserDisplayNameVersions,
245 pub is_administrator: bool,
246 pub links: VersionedLinks,
247}
248
249impl User {
250 pub fn to_short(&self) -> ShortUser {
251 ShortUser::from(self.clone())
253 }
254}
255
256declare_new_string! {
257 pub struct UserDisplayName(String);
258 pub type ParseError = UserDisplayNameParseError;
259 const PATTERN = r"^[\p{Letter}_ ()][\p{Letter}_ ()0-9]*$";
260 const SQL_NAME = "user_display_name";
261}
262
263#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
264#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
265pub struct UserDisplayNameVersion {
266 pub value: UserDisplayName,
267}
268
269#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
270#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
271pub struct UserDisplayNameVersions {
272 pub current: UserDisplayNameVersion,
273}
274
275#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(tag = "method"))]
276#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
277pub enum LinkToDinoparcOptions {
278 Credentials(LinkToDinoparcWithCredentialsOptions),
279 Ref(LinkToDinoparcWithRefOptions),
280}
281
282#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
283#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
284pub struct LinkToDinoparcWithCredentialsOptions {
285 pub user_id: UserId,
286 pub dinoparc_server: DinoparcServer,
287 pub dinoparc_username: DinoparcUsername,
288 pub dinoparc_password: DinoparcPassword,
289}
290
291#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
292#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
293pub struct LinkToDinoparcWithRefOptions {
294 pub user_id: UserId,
295 pub dinoparc_server: DinoparcServer,
296 pub dinoparc_user_id: DinoparcUserId,
297}
298
299#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
300#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
301pub struct UnlinkFromDinoparcOptions {
302 pub user_id: UserId,
303 pub dinoparc_server: DinoparcServer,
304 pub dinoparc_user_id: DinoparcUserId,
305}
306
307#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(tag = "method"))]
308#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
309pub enum LinkToHammerfestOptions {
310 Credentials(LinkToHammerfestWithCredentialsOptions),
311 SessionKey(LinkToHammerfestWithSessionKeyOptions),
312 Ref(LinkToHammerfestWithRefOptions),
313}
314
315#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
316#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
317pub struct LinkToHammerfestWithCredentialsOptions {
318 pub user_id: UserId,
319 pub hammerfest_server: HammerfestServer,
320 pub hammerfest_username: HammerfestUsername,
321 pub hammerfest_password: HammerfestPassword,
322}
323
324#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
325#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
326pub struct LinkToHammerfestWithSessionKeyOptions {
327 pub user_id: UserId,
328 pub hammerfest_server: HammerfestServer,
329 pub hammerfest_session_key: HammerfestSessionKey,
330}
331
332#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
333#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
334pub struct LinkToHammerfestWithRefOptions {
335 pub user_id: UserId,
336 pub hammerfest_server: HammerfestServer,
337 pub hammerfest_user_id: HammerfestUserId,
338}
339
340#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
341#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
342pub struct UnlinkFromHammerfestOptions {
343 pub user_id: UserId,
344 pub hammerfest_server: HammerfestServer,
345 pub hammerfest_user_id: HammerfestUserId,
346}
347
348#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(tag = "method"))]
349#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
350pub enum LinkToTwinoidOptions {
351 Oauth(LinkToTwinoidWithOauthOptions),
352 Ref(LinkToTwinoidWithRefOptions),
353}
354
355#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
356#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
357pub struct LinkToTwinoidWithOauthOptions {
358 pub user_id: UserId,
359 pub access_token: RfcOauthAccessToken,
360}
361
362#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
363#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
364pub struct LinkToTwinoidWithRefOptions {
365 pub user_id: UserId,
366 pub twinoid_user_id: TwinoidUserId,
367}
368
369#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
370#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
371pub struct UnlinkFromTwinoidOptions {
372 pub user_id: UserId,
373 pub twinoid_user_id: TwinoidUserId,
374}
375
376declare_new_uuid! {
377 pub struct UserId(Uuid);
378 pub type ParseError = UserIdParseError;
379 const SQL_NAME = "user_id";
380}
381
382#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
383#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
384#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
385pub struct UserIdRef {
386 pub id: UserId,
387}
388
389impl UserIdRef {
390 pub const fn new(id: UserId) -> Self {
391 Self { id }
392 }
393}
394
395impl From<UserId> for UserIdRef {
396 fn from(id: UserId) -> Self {
397 Self::new(id)
398 }
399}
400
401#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
402#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
403#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
404pub struct UserUsernameRef {
405 pub username: Username,
406}
407
408#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
409#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
410#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
411pub struct UserEmailRef {
412 pub email: EmailAddress,
413}
414
415#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
416#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
417pub enum UserRef {
418 Id(UserIdRef),
419 Username(UserUsernameRef),
420 Email(UserEmailRef),
421}
422
423impl From<UserId> for UserRef {
424 fn from(id: UserId) -> Self {
425 Self::Id(id.into())
426 }
427}
428
429impl From<UserIdRef> for UserRef {
430 fn from(r: UserIdRef) -> Self {
431 Self::Id(r)
432 }
433}
434
435impl From<UserUsernameRef> for UserRef {
436 fn from(r: UserUsernameRef) -> Self {
437 Self::Username(r)
438 }
439}
440
441impl From<UserEmailRef> for UserRef {
442 fn from(r: UserEmailRef) -> Self {
443 Self::Email(r)
444 }
445}
446
447declare_new_string! {
448 pub struct Username(String);
449 pub type ParseError = UsernameParseError;
450 const PATTERN = "^[a-z_][a-z0-9_]{1,31}$";
451 const SQL_NAME = "username";
452}
453
454pub static USERNAME_LOCK_DURATION: Lazy<Duration> = Lazy::new(|| Duration::days(7));
455pub static USER_DISPLAY_NAME_LOCK_DURATION: Lazy<Duration> = Lazy::new(|| Duration::days(30));
456pub static USER_PASSWORD_LOCK_DURATION: Lazy<Duration> = Lazy::new(|| Duration::minutes(10));
457
458#[derive(Debug, thiserror::Error)]
459pub enum RawUpdateUserError {
460 #[error("Failed to find user to update for ref: {:?}", .0)]
461 NotFound(UserIdRef),
462 #[error("Failed to update user {:?}, display_name locked during {:?}, current time is {}", .0, .1, .2)]
463 LockedDisplayName(UserIdRef, FinitePeriod, Instant),
464 #[error("Failed to update user {:?}, username locked during {:?}, current time is {}", .0, .1, .2)]
465 LockedUsername(UserIdRef, FinitePeriod, Instant),
466 #[error("Failed to update user {:?}, password locked during {:?}, current time is {}", .0, .1, .2)]
467 LockedPassword(UserIdRef, FinitePeriod, Instant),
468 #[error(transparent)]
469 Other(AnyError),
470}
471
472impl PartialEq for RawUpdateUserError {
473 fn eq(&self, other: &Self) -> bool {
474 match (self, other) {
475 (RawUpdateUserError::NotFound(l), RawUpdateUserError::NotFound(r)) if l == r => true,
476 (RawUpdateUserError::LockedDisplayName(l0, l1, l2), RawUpdateUserError::LockedDisplayName(r0, r1, r2))
477 if (l0, l1, l2) == (r0, r1, r2) =>
478 {
479 true
480 }
481 (RawUpdateUserError::LockedUsername(l0, l1, l2), RawUpdateUserError::LockedUsername(r0, r1, r2))
482 if (l0, l1, l2) == (r0, r1, r2) =>
483 {
484 true
485 }
486 (RawUpdateUserError::LockedPassword(l0, l1, l2), RawUpdateUserError::LockedPassword(r0, r1, r2))
487 if (l0, l1, l2) == (r0, r1, r2) =>
488 {
489 true
490 }
491 _ => false,
492 }
493 }
494}
495
496impl RawUpdateUserError {
497 pub fn other<E: 'static + Error + Send + Sync>(e: E) -> Self {
498 Self::Other(Box::new(e))
499 }
500}
501
502#[derive(Debug, thiserror::Error)]
503pub enum DeleteUserError {
504 #[error("Failed to find user to delete for ref: {:?}", .0)]
505 NotFound(UserIdRef),
506 #[error(transparent)]
507 Other(AnyError),
508}
509
510impl PartialEq for DeleteUserError {
511 fn eq(&self, other: &Self) -> bool {
512 #[allow(clippy::match_like_matches_macro)]
513 match (self, other) {
514 (DeleteUserError::NotFound(l), DeleteUserError::NotFound(r)) if l == r => true,
515 _ => false,
516 }
517 }
518}
519
520impl DeleteUserError {
521 pub fn other<E: 'static + Error + Send + Sync>(e: E) -> Self {
522 Self::Other(Box::new(e))
523 }
524}
525
526#[async_trait]
527#[auto_impl(&, Arc)]
528pub trait UserStore: Send + Sync {
529 async fn create_user(&self, options: &CreateUserOptions) -> Result<CompleteSimpleUser, AnyError>;
530
531 async fn get_user(&self, options: &GetUserOptions) -> Result<Option<RawGetUserResult>, AnyError>;
532
533 async fn get_short_user(&self, options: &GetShortUserOptions) -> Result<Option<ShortUser>, AnyError>;
535
536 async fn get_user_with_password(&self, options: &GetUserOptions) -> Result<Option<ShortUserWithPassword>, AnyError>;
537
538 async fn update_user(&self, options: &RawUpdateUserOptions) -> Result<CompleteSimpleUser, RawUpdateUserError>;
539
540 async fn hard_delete_user(&self, user_ref: UserIdRef) -> Result<(), DeleteUserError>;
541}
542
543#[cfg(test)]
544mod test {
545 #[cfg(feature = "serde")]
546 use crate::core::Instant;
547 #[cfg(feature = "serde")]
548 use crate::password::PasswordHash;
549 #[cfg(feature = "serde")]
550 use crate::user::{
551 CompleteSimpleUser, RawGetUserResult, RawUpdateUserPatch, ShortUser, SimpleUser, UserDisplayNameVersion,
552 UserDisplayNameVersions,
553 };
554 #[cfg(feature = "serde")]
555 use std::fs;
556
557 #[cfg(feature = "serde")]
558 fn get_short_user_demurgos() -> ShortUser {
559 ShortUser {
560 id: "9f310484-963b-446b-af69-797feec6813f".parse().unwrap(),
561 display_name: UserDisplayNameVersions {
562 current: UserDisplayNameVersion {
563 value: "Demurgos".parse().unwrap(),
564 },
565 },
566 }
567 }
568
569 #[cfg(feature = "serde")]
570 #[test]
571 fn read_short_user_demurgos() {
572 let s = fs::read_to_string("../../test-resources/core/user/short-user/demurgos/value.json").unwrap();
573 let actual: ShortUser = serde_json::from_str(&s).unwrap();
574 let expected = get_short_user_demurgos();
575 assert_eq!(actual, expected);
576 }
577
578 #[cfg(feature = "serde")]
579 #[test]
580 fn write_short_user_demurgos() {
581 let value = get_short_user_demurgos();
582 let actual: String = serde_json::to_string_pretty(&value).unwrap();
583 let expected = fs::read_to_string("../../test-resources/core/user/short-user/demurgos/value.json").unwrap();
584 assert_eq!(&actual, expected.trim());
585 }
586
587 #[cfg(feature = "serde")]
588 fn get_get_user_result_short() -> RawGetUserResult {
589 RawGetUserResult::Short(ShortUser {
590 id: "e9c17533-633e-4f60-be9e-72883ae0174a".parse().unwrap(),
591 display_name: UserDisplayNameVersions {
592 current: UserDisplayNameVersion {
593 value: "Alice".parse().unwrap(),
594 },
595 },
596 })
597 }
598
599 #[cfg(feature = "serde")]
600 #[test]
601 fn read_get_user_result_short() {
602 let s = fs::read_to_string("../../test-resources/core/user/get-user-result/short/value.json").unwrap();
603 let actual: RawGetUserResult = serde_json::from_str(&s).unwrap();
604 let expected = get_get_user_result_short();
605 assert_eq!(actual, expected);
606 }
607
608 #[cfg(feature = "serde")]
609 #[test]
610 fn write_get_user_result_short() {
611 let value = get_get_user_result_short();
612 let actual: String = serde_json::to_string_pretty(&value).unwrap();
613 let expected = fs::read_to_string("../../test-resources/core/user/get-user-result/short/value.json").unwrap();
614 assert_eq!(&actual, expected.trim());
615 }
616
617 #[cfg(feature = "serde")]
618 fn get_get_user_result_default() -> RawGetUserResult {
619 RawGetUserResult::Default(SimpleUser {
620 id: "28dbb0bf-0fdc-40fe-ae5a-dde193f9fea8".parse().unwrap(),
621 display_name: UserDisplayNameVersions {
622 current: UserDisplayNameVersion {
623 value: "Alice".parse().unwrap(),
624 },
625 },
626 created_at: Instant::ymd_hms_milli(2021, 1, 15, 14, 17, 14, 15),
627 is_administrator: true,
628 })
629 }
630
631 #[cfg(feature = "serde")]
632 #[test]
633 fn read_get_user_result_default() {
634 let s = fs::read_to_string("../../test-resources/core/user/get-user-result/default/value.json").unwrap();
635 let actual: RawGetUserResult = serde_json::from_str(&s).unwrap();
636 let expected = get_get_user_result_default();
637 assert_eq!(actual, expected);
638 }
639
640 #[cfg(feature = "serde")]
641 #[test]
642 fn write_get_user_result_default() {
643 let value = get_get_user_result_default();
644 let actual: String = serde_json::to_string_pretty(&value).unwrap();
645 let expected = fs::read_to_string("../../test-resources/core/user/get-user-result/default/value.json").unwrap();
646 assert_eq!(&actual, expected.trim());
647 }
648
649 #[cfg(feature = "serde")]
650 fn get_get_user_result_complete() -> RawGetUserResult {
651 RawGetUserResult::Complete(CompleteSimpleUser {
652 id: "abeb9363-2035-4c20-9bb8-21edfb432cbf".parse().unwrap(),
653 display_name: UserDisplayNameVersions {
654 current: UserDisplayNameVersion {
655 value: "Alice".parse().unwrap(),
656 },
657 },
658 created_at: Instant::ymd_hms_milli(2021, 1, 15, 14, 17, 14, 15),
659 is_administrator: true,
660 username: Some("alice".parse().unwrap()),
661 email_address: None,
662 })
663 }
664
665 #[cfg(feature = "serde")]
666 #[test]
667 fn read_get_user_result_complete() {
668 let s = fs::read_to_string("../../test-resources/core/user/get-user-result/complete/value.json").unwrap();
669 let actual: RawGetUserResult = serde_json::from_str(&s).unwrap();
670 let expected = get_get_user_result_complete();
671 assert_eq!(actual, expected);
672 }
673
674 #[cfg(feature = "serde")]
675 #[test]
676 fn write_get_user_result_complete() {
677 let value = get_get_user_result_complete();
678 let actual: String = serde_json::to_string_pretty(&value).unwrap();
679 let expected = fs::read_to_string("../../test-resources/core/user/get-user-result/complete/value.json").unwrap();
680 assert_eq!(&actual, expected.trim());
681 }
682
683 #[cfg(feature = "serde")]
684 fn get_update_user_patch_no_op() -> RawUpdateUserPatch {
685 RawUpdateUserPatch {
686 display_name: None,
687 username: None,
688 password: None,
689 }
690 }
691
692 #[cfg(feature = "serde")]
693 #[test]
694 fn read_update_user_patch_no_op() {
695 let s = fs::read_to_string("../../test-resources/core/user/update-user-patch/no-op/value.json").unwrap();
696 let actual: RawUpdateUserPatch = serde_json::from_str(&s).unwrap();
697 let expected = get_update_user_patch_no_op();
698 assert_eq!(actual, expected);
699 }
700
701 #[cfg(feature = "serde")]
702 #[test]
703 fn write_update_user_patch_no_op() {
704 let value = get_update_user_patch_no_op();
705 let actual: String = serde_json::to_string_pretty(&value).unwrap();
706 let expected = fs::read_to_string("../../test-resources/core/user/update-user-patch/no-op/value.json").unwrap();
707 assert_eq!(&actual, expected.trim());
708 }
709
710 #[cfg(feature = "serde")]
711 fn get_update_user_patch_remove_username() -> RawUpdateUserPatch {
712 RawUpdateUserPatch {
713 display_name: None,
714 username: Some(None),
715 password: None,
716 }
717 }
718
719 #[cfg(feature = "serde")]
720 #[test]
721 fn read_update_user_patch_remove_username() {
722 let s = fs::read_to_string("../../test-resources/core/user/update-user-patch/remove-username/value.json").unwrap();
723 let actual: RawUpdateUserPatch = serde_json::from_str(&s).unwrap();
724 let expected = get_update_user_patch_remove_username();
725 assert_eq!(actual, expected);
726 }
727
728 #[cfg(feature = "serde")]
729 #[test]
730 fn write_update_user_patch_remove_username() {
731 let value = get_update_user_patch_remove_username();
732 let actual: String = serde_json::to_string_pretty(&value).unwrap();
733 let expected =
734 fs::read_to_string("../../test-resources/core/user/update-user-patch/remove-username/value.json").unwrap();
735 assert_eq!(&actual, expected.trim());
736 }
737
738 #[cfg(feature = "serde")]
739 fn get_update_user_patch_set_all() -> RawUpdateUserPatch {
740 let hash: Vec<u8> = hex::decode("736372797074000c0000000800000001c5ec1067adb434a19cb471dcfc13a8cec8c6e935ec7e14eda9f51a386924eeeb9fce39bb3d36f6101cc06189da63e0513a54553efbee9d2a058bafbda5231093c4ae5e9b3f87a2d002fa49ff75b868fd").unwrap();
741 RawUpdateUserPatch {
742 display_name: Some("Demurgos".parse().unwrap()),
743 username: Some(Some("demurgos".parse().unwrap())),
744 password: Some(Some(PasswordHash::from(&hash[..]))),
745 }
746 }
747
748 #[cfg(feature = "serde")]
749 #[test]
750 fn read_update_user_patch_set_all() {
751 let s = fs::read_to_string("../../test-resources/core/user/update-user-patch/set-all/value.json").unwrap();
752 let actual: RawUpdateUserPatch = serde_json::from_str(&s).unwrap();
753 let expected = get_update_user_patch_set_all();
754 assert_eq!(actual, expected);
755 }
756
757 #[cfg(feature = "serde")]
758 #[test]
759 fn write_update_user_patch_set_all() {
760 let value = get_update_user_patch_set_all();
761 let actual: String = serde_json::to_string_pretty(&value).unwrap();
762 let expected = fs::read_to_string("../../test-resources/core/user/update-user-patch/set-all/value.json").unwrap();
763 assert_eq!(&actual, expected.trim());
764 }
765
766 #[cfg(feature = "serde")]
767 fn get_update_user_patch_set_username() -> RawUpdateUserPatch {
768 RawUpdateUserPatch {
769 display_name: None,
770 username: Some(Some("demurgos".parse().unwrap())),
771 password: None,
772 }
773 }
774
775 #[cfg(feature = "serde")]
776 #[test]
777 fn read_update_user_patch_set_username() {
778 let s = fs::read_to_string("../../test-resources/core/user/update-user-patch/set-username/value.json").unwrap();
779 let actual: RawUpdateUserPatch = serde_json::from_str(&s).unwrap();
780 let expected = get_update_user_patch_set_username();
781 assert_eq!(actual, expected);
782 }
783
784 #[cfg(feature = "serde")]
785 #[test]
786 fn write_update_user_patch_set_username() {
787 let value = get_update_user_patch_set_username();
788 let actual: String = serde_json::to_string_pretty(&value).unwrap();
789 let expected =
790 fs::read_to_string("../../test-resources/core/user/update-user-patch/set-username/value.json").unwrap();
791 assert_eq!(&actual, expected.trim());
792 }
793}