use crate::core::{FinitePeriod, Instant};
use crate::dinoparc::{DinoparcPassword, DinoparcServer, DinoparcUserId, DinoparcUsername};
use crate::email::EmailAddress;
use crate::hammerfest::{
HammerfestPassword, HammerfestServer, HammerfestSessionKey, HammerfestUserId, HammerfestUsername,
};
use crate::link::VersionedLinks;
use crate::oauth::RfcOauthAccessToken;
use crate::password::{Password, PasswordHash};
use crate::twinoid::TwinoidUserId;
use crate::types::AnyError;
use async_trait::async_trait;
use auto_impl::auto_impl;
use chrono::Duration;
#[cfg(feature = "serde")]
use etwin_serde_tools::{deserialize_explicit_option, deserialize_nested_option, Deserialize, Serialize};
use once_cell::sync::Lazy;
use std::error::Error;
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CompleteSimpleUser {
pub id: UserId,
pub created_at: Instant,
pub display_name: UserDisplayNameVersions,
pub is_administrator: bool,
#[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_explicit_option"))]
pub username: Option<Username>,
#[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize_explicit_option"))]
pub email_address: Option<EmailAddress>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CompleteUser {
pub id: UserId,
pub created_at: Instant,
pub display_name: UserDisplayNameVersions,
pub is_administrator: bool,
pub links: VersionedLinks,
pub username: Option<Username>,
pub email_address: Option<EmailAddress>,
pub has_password: bool,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CreateUserOptions {
pub display_name: UserDisplayName,
pub email: Option<EmailAddress>,
pub username: Option<Username>,
pub password: Option<PasswordHash>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UpdateUserOptions {
pub r#ref: UserIdRef,
pub patch: UpdateUserPatch,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UpdateUserPatch {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub display_name: Option<UserDisplayName>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_nested_option"))]
pub username: Option<Option<Username>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_nested_option"))]
pub password: Option<Option<Password>>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RawUpdateUserOptions {
pub actor: UserIdRef,
pub r#ref: UserIdRef,
pub patch: RawUpdateUserPatch,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RawUpdateUserPatch {
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
pub display_name: Option<UserDisplayName>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_nested_option"))]
pub username: Option<Option<Username>>,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_nested_option"))]
pub password: Option<Option<PasswordHash>>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(tag = "type"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum UserFields {
CompleteIfSelf { self_user_id: UserId },
Complete,
Default,
Short,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GetUserOptions {
pub r#ref: UserRef,
pub fields: UserFields,
pub time: Option<Instant>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GetShortUserOptions {
pub r#ref: UserRef,
pub time: Option<Instant>,
}
impl From<UserRef> for GetShortUserOptions {
fn from(r#ref: UserRef) -> Self {
Self { r#ref, time: None }
}
}
impl From<UserIdRef> for GetShortUserOptions {
fn from(r: UserIdRef) -> Self {
let r: UserRef = r.into();
r.into()
}
}
impl From<UserId> for GetShortUserOptions {
fn from(id: UserId) -> Self {
let r: UserRef = id.into();
r.into()
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum GetUserResult {
Complete(CompleteUser),
Default(User),
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum RawGetUserResult {
Complete(CompleteSimpleUser),
Default(SimpleUser),
Short(ShortUser),
}
impl RawGetUserResult {
pub fn id(&self) -> UserId {
match self {
Self::Complete(u) => u.id,
Self::Default(u) => u.id,
Self::Short(u) => u.id,
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ShortUser {
pub id: UserId,
pub display_name: UserDisplayNameVersions,
}
impl ShortUser {
pub const fn as_ref(&self) -> UserIdRef {
UserIdRef { id: self.id }
}
}
impl From<SimpleUser> for ShortUser {
fn from(user: SimpleUser) -> Self {
Self {
id: user.id,
display_name: user.display_name,
}
}
}
impl From<CompleteSimpleUser> for ShortUser {
fn from(user: CompleteSimpleUser) -> Self {
Self {
id: user.id,
display_name: user.display_name,
}
}
}
impl From<User> for ShortUser {
fn from(user: User) -> Self {
Self {
id: user.id,
display_name: user.display_name,
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ShortUserWithPassword {
pub id: UserId,
pub display_name: UserDisplayNameVersions,
pub password: Option<PasswordHash>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SimpleUser {
pub id: UserId,
pub created_at: Instant,
pub display_name: UserDisplayNameVersions,
pub is_administrator: bool,
}
impl From<CompleteSimpleUser> for SimpleUser {
fn from(user: CompleteSimpleUser) -> Self {
Self {
id: user.id,
created_at: user.created_at,
display_name: user.display_name,
is_administrator: user.is_administrator,
}
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct User {
pub id: UserId,
pub created_at: Instant,
pub display_name: UserDisplayNameVersions,
pub is_administrator: bool,
pub links: VersionedLinks,
}
impl User {
pub fn to_short(&self) -> ShortUser {
ShortUser::from(self.clone())
}
}
declare_new_string! {
pub struct UserDisplayName(String);
pub type ParseError = UserDisplayNameParseError;
const PATTERN = r"^[\p{Letter}_ ()][\p{Letter}_ ()0-9]*$";
const SQL_NAME = "user_display_name";
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UserDisplayNameVersion {
pub value: UserDisplayName,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UserDisplayNameVersions {
pub current: UserDisplayNameVersion,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(tag = "method"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LinkToDinoparcOptions {
Credentials(LinkToDinoparcWithCredentialsOptions),
Ref(LinkToDinoparcWithRefOptions),
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinkToDinoparcWithCredentialsOptions {
pub user_id: UserId,
pub dinoparc_server: DinoparcServer,
pub dinoparc_username: DinoparcUsername,
pub dinoparc_password: DinoparcPassword,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinkToDinoparcWithRefOptions {
pub user_id: UserId,
pub dinoparc_server: DinoparcServer,
pub dinoparc_user_id: DinoparcUserId,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnlinkFromDinoparcOptions {
pub user_id: UserId,
pub dinoparc_server: DinoparcServer,
pub dinoparc_user_id: DinoparcUserId,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(tag = "method"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LinkToHammerfestOptions {
Credentials(LinkToHammerfestWithCredentialsOptions),
SessionKey(LinkToHammerfestWithSessionKeyOptions),
Ref(LinkToHammerfestWithRefOptions),
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinkToHammerfestWithCredentialsOptions {
pub user_id: UserId,
pub hammerfest_server: HammerfestServer,
pub hammerfest_username: HammerfestUsername,
pub hammerfest_password: HammerfestPassword,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinkToHammerfestWithSessionKeyOptions {
pub user_id: UserId,
pub hammerfest_server: HammerfestServer,
pub hammerfest_session_key: HammerfestSessionKey,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinkToHammerfestWithRefOptions {
pub user_id: UserId,
pub hammerfest_server: HammerfestServer,
pub hammerfest_user_id: HammerfestUserId,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnlinkFromHammerfestOptions {
pub user_id: UserId,
pub hammerfest_server: HammerfestServer,
pub hammerfest_user_id: HammerfestUserId,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(tag = "method"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum LinkToTwinoidOptions {
Oauth(LinkToTwinoidWithOauthOptions),
Ref(LinkToTwinoidWithRefOptions),
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinkToTwinoidWithOauthOptions {
pub user_id: UserId,
pub access_token: RfcOauthAccessToken,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinkToTwinoidWithRefOptions {
pub user_id: UserId,
pub twinoid_user_id: TwinoidUserId,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnlinkFromTwinoidOptions {
pub user_id: UserId,
pub twinoid_user_id: TwinoidUserId,
}
declare_new_uuid! {
pub struct UserId(Uuid);
pub type ParseError = UserIdParseError;
const SQL_NAME = "user_id";
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UserIdRef {
pub id: UserId,
}
impl UserIdRef {
pub const fn new(id: UserId) -> Self {
Self { id }
}
}
impl From<UserId> for UserIdRef {
fn from(id: UserId) -> Self {
Self::new(id)
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UserUsernameRef {
pub username: Username,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "type", rename = "User"))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UserEmailRef {
pub email: EmailAddress,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))]
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum UserRef {
Id(UserIdRef),
Username(UserUsernameRef),
Email(UserEmailRef),
}
impl From<UserId> for UserRef {
fn from(id: UserId) -> Self {
Self::Id(id.into())
}
}
impl From<UserIdRef> for UserRef {
fn from(r: UserIdRef) -> Self {
Self::Id(r)
}
}
impl From<UserUsernameRef> for UserRef {
fn from(r: UserUsernameRef) -> Self {
Self::Username(r)
}
}
impl From<UserEmailRef> for UserRef {
fn from(r: UserEmailRef) -> Self {
Self::Email(r)
}
}
declare_new_string! {
pub struct Username(String);
pub type ParseError = UsernameParseError;
const PATTERN = "^[a-z_][a-z0-9_]{1,31}$";
const SQL_NAME = "username";
}
pub static USERNAME_LOCK_DURATION: Lazy<Duration> = Lazy::new(|| Duration::days(7));
pub static USER_DISPLAY_NAME_LOCK_DURATION: Lazy<Duration> = Lazy::new(|| Duration::days(30));
pub static USER_PASSWORD_LOCK_DURATION: Lazy<Duration> = Lazy::new(|| Duration::minutes(10));
#[derive(Debug, thiserror::Error)]
pub enum RawUpdateUserError {
#[error("Failed to find user to update for ref: {:?}", .0)]
NotFound(UserIdRef),
#[error("Failed to update user {:?}, display_name locked during {:?}, current time is {}", .0, .1, .2)]
LockedDisplayName(UserIdRef, FinitePeriod, Instant),
#[error("Failed to update user {:?}, username locked during {:?}, current time is {}", .0, .1, .2)]
LockedUsername(UserIdRef, FinitePeriod, Instant),
#[error("Failed to update user {:?}, password locked during {:?}, current time is {}", .0, .1, .2)]
LockedPassword(UserIdRef, FinitePeriod, Instant),
#[error(transparent)]
Other(AnyError),
}
impl PartialEq for RawUpdateUserError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(RawUpdateUserError::NotFound(l), RawUpdateUserError::NotFound(r)) if l == r => true,
(RawUpdateUserError::LockedDisplayName(l0, l1, l2), RawUpdateUserError::LockedDisplayName(r0, r1, r2))
if (l0, l1, l2) == (r0, r1, r2) =>
{
true
}
(RawUpdateUserError::LockedUsername(l0, l1, l2), RawUpdateUserError::LockedUsername(r0, r1, r2))
if (l0, l1, l2) == (r0, r1, r2) =>
{
true
}
(RawUpdateUserError::LockedPassword(l0, l1, l2), RawUpdateUserError::LockedPassword(r0, r1, r2))
if (l0, l1, l2) == (r0, r1, r2) =>
{
true
}
_ => false,
}
}
}
impl RawUpdateUserError {
pub fn other<E: 'static + Error + Send + Sync>(e: E) -> Self {
Self::Other(Box::new(e))
}
}
#[derive(Debug, thiserror::Error)]
pub enum DeleteUserError {
#[error("Failed to find user to delete for ref: {:?}", .0)]
NotFound(UserIdRef),
#[error(transparent)]
Other(AnyError),
}
impl PartialEq for DeleteUserError {
fn eq(&self, other: &Self) -> bool {
#[allow(clippy::match_like_matches_macro)]
match (self, other) {
(DeleteUserError::NotFound(l), DeleteUserError::NotFound(r)) if l == r => true,
_ => false,
}
}
}
impl DeleteUserError {
pub fn other<E: 'static + Error + Send + Sync>(e: E) -> Self {
Self::Other(Box::new(e))
}
}
#[async_trait]
#[auto_impl(&, Arc)]
pub trait UserStore: Send + Sync {
async fn create_user(&self, options: &CreateUserOptions) -> Result<CompleteSimpleUser, AnyError>;
async fn get_user(&self, options: &GetUserOptions) -> Result<Option<RawGetUserResult>, AnyError>;
async fn get_short_user(&self, options: &GetShortUserOptions) -> Result<Option<ShortUser>, AnyError>;
async fn get_user_with_password(&self, options: &GetUserOptions) -> Result<Option<ShortUserWithPassword>, AnyError>;
async fn update_user(&self, options: &RawUpdateUserOptions) -> Result<CompleteSimpleUser, RawUpdateUserError>;
async fn hard_delete_user(&self, user_ref: UserIdRef) -> Result<(), DeleteUserError>;
}
#[cfg(test)]
mod test {
#[cfg(feature = "serde")]
use crate::core::Instant;
#[cfg(feature = "serde")]
use crate::password::PasswordHash;
#[cfg(feature = "serde")]
use crate::user::{
CompleteSimpleUser, RawGetUserResult, RawUpdateUserPatch, ShortUser, SimpleUser, UserDisplayNameVersion,
UserDisplayNameVersions,
};
#[cfg(feature = "serde")]
use std::fs;
#[cfg(feature = "serde")]
fn get_short_user_demurgos() -> ShortUser {
ShortUser {
id: "9f310484-963b-446b-af69-797feec6813f".parse().unwrap(),
display_name: UserDisplayNameVersions {
current: UserDisplayNameVersion {
value: "Demurgos".parse().unwrap(),
},
},
}
}
#[cfg(feature = "serde")]
#[test]
fn read_short_user_demurgos() {
let s = fs::read_to_string("../../test-resources/core/user/short-user/demurgos/value.json").unwrap();
let actual: ShortUser = serde_json::from_str(&s).unwrap();
let expected = get_short_user_demurgos();
assert_eq!(actual, expected);
}
#[cfg(feature = "serde")]
#[test]
fn write_short_user_demurgos() {
let value = get_short_user_demurgos();
let actual: String = serde_json::to_string_pretty(&value).unwrap();
let expected = fs::read_to_string("../../test-resources/core/user/short-user/demurgos/value.json").unwrap();
assert_eq!(&actual, expected.trim());
}
#[cfg(feature = "serde")]
fn get_get_user_result_short() -> RawGetUserResult {
RawGetUserResult::Short(ShortUser {
id: "e9c17533-633e-4f60-be9e-72883ae0174a".parse().unwrap(),
display_name: UserDisplayNameVersions {
current: UserDisplayNameVersion {
value: "Alice".parse().unwrap(),
},
},
})
}
#[cfg(feature = "serde")]
#[test]
fn read_get_user_result_short() {
let s = fs::read_to_string("../../test-resources/core/user/get-user-result/short/value.json").unwrap();
let actual: RawGetUserResult = serde_json::from_str(&s).unwrap();
let expected = get_get_user_result_short();
assert_eq!(actual, expected);
}
#[cfg(feature = "serde")]
#[test]
fn write_get_user_result_short() {
let value = get_get_user_result_short();
let actual: String = serde_json::to_string_pretty(&value).unwrap();
let expected = fs::read_to_string("../../test-resources/core/user/get-user-result/short/value.json").unwrap();
assert_eq!(&actual, expected.trim());
}
#[cfg(feature = "serde")]
fn get_get_user_result_default() -> RawGetUserResult {
RawGetUserResult::Default(SimpleUser {
id: "28dbb0bf-0fdc-40fe-ae5a-dde193f9fea8".parse().unwrap(),
display_name: UserDisplayNameVersions {
current: UserDisplayNameVersion {
value: "Alice".parse().unwrap(),
},
},
created_at: Instant::ymd_hms_milli(2021, 1, 15, 14, 17, 14, 15),
is_administrator: true,
})
}
#[cfg(feature = "serde")]
#[test]
fn read_get_user_result_default() {
let s = fs::read_to_string("../../test-resources/core/user/get-user-result/default/value.json").unwrap();
let actual: RawGetUserResult = serde_json::from_str(&s).unwrap();
let expected = get_get_user_result_default();
assert_eq!(actual, expected);
}
#[cfg(feature = "serde")]
#[test]
fn write_get_user_result_default() {
let value = get_get_user_result_default();
let actual: String = serde_json::to_string_pretty(&value).unwrap();
let expected = fs::read_to_string("../../test-resources/core/user/get-user-result/default/value.json").unwrap();
assert_eq!(&actual, expected.trim());
}
#[cfg(feature = "serde")]
fn get_get_user_result_complete() -> RawGetUserResult {
RawGetUserResult::Complete(CompleteSimpleUser {
id: "abeb9363-2035-4c20-9bb8-21edfb432cbf".parse().unwrap(),
display_name: UserDisplayNameVersions {
current: UserDisplayNameVersion {
value: "Alice".parse().unwrap(),
},
},
created_at: Instant::ymd_hms_milli(2021, 1, 15, 14, 17, 14, 15),
is_administrator: true,
username: Some("alice".parse().unwrap()),
email_address: None,
})
}
#[cfg(feature = "serde")]
#[test]
fn read_get_user_result_complete() {
let s = fs::read_to_string("../../test-resources/core/user/get-user-result/complete/value.json").unwrap();
let actual: RawGetUserResult = serde_json::from_str(&s).unwrap();
let expected = get_get_user_result_complete();
assert_eq!(actual, expected);
}
#[cfg(feature = "serde")]
#[test]
fn write_get_user_result_complete() {
let value = get_get_user_result_complete();
let actual: String = serde_json::to_string_pretty(&value).unwrap();
let expected = fs::read_to_string("../../test-resources/core/user/get-user-result/complete/value.json").unwrap();
assert_eq!(&actual, expected.trim());
}
#[cfg(feature = "serde")]
fn get_update_user_patch_no_op() -> RawUpdateUserPatch {
RawUpdateUserPatch {
display_name: None,
username: None,
password: None,
}
}
#[cfg(feature = "serde")]
#[test]
fn read_update_user_patch_no_op() {
let s = fs::read_to_string("../../test-resources/core/user/update-user-patch/no-op/value.json").unwrap();
let actual: RawUpdateUserPatch = serde_json::from_str(&s).unwrap();
let expected = get_update_user_patch_no_op();
assert_eq!(actual, expected);
}
#[cfg(feature = "serde")]
#[test]
fn write_update_user_patch_no_op() {
let value = get_update_user_patch_no_op();
let actual: String = serde_json::to_string_pretty(&value).unwrap();
let expected = fs::read_to_string("../../test-resources/core/user/update-user-patch/no-op/value.json").unwrap();
assert_eq!(&actual, expected.trim());
}
#[cfg(feature = "serde")]
fn get_update_user_patch_remove_username() -> RawUpdateUserPatch {
RawUpdateUserPatch {
display_name: None,
username: Some(None),
password: None,
}
}
#[cfg(feature = "serde")]
#[test]
fn read_update_user_patch_remove_username() {
let s = fs::read_to_string("../../test-resources/core/user/update-user-patch/remove-username/value.json").unwrap();
let actual: RawUpdateUserPatch = serde_json::from_str(&s).unwrap();
let expected = get_update_user_patch_remove_username();
assert_eq!(actual, expected);
}
#[cfg(feature = "serde")]
#[test]
fn write_update_user_patch_remove_username() {
let value = get_update_user_patch_remove_username();
let actual: String = serde_json::to_string_pretty(&value).unwrap();
let expected =
fs::read_to_string("../../test-resources/core/user/update-user-patch/remove-username/value.json").unwrap();
assert_eq!(&actual, expected.trim());
}
#[cfg(feature = "serde")]
fn get_update_user_patch_set_all() -> RawUpdateUserPatch {
let hash: Vec<u8> = hex::decode("736372797074000c0000000800000001c5ec1067adb434a19cb471dcfc13a8cec8c6e935ec7e14eda9f51a386924eeeb9fce39bb3d36f6101cc06189da63e0513a54553efbee9d2a058bafbda5231093c4ae5e9b3f87a2d002fa49ff75b868fd").unwrap();
RawUpdateUserPatch {
display_name: Some("Demurgos".parse().unwrap()),
username: Some(Some("demurgos".parse().unwrap())),
password: Some(Some(PasswordHash::from(&hash[..]))),
}
}
#[cfg(feature = "serde")]
#[test]
fn read_update_user_patch_set_all() {
let s = fs::read_to_string("../../test-resources/core/user/update-user-patch/set-all/value.json").unwrap();
let actual: RawUpdateUserPatch = serde_json::from_str(&s).unwrap();
let expected = get_update_user_patch_set_all();
assert_eq!(actual, expected);
}
#[cfg(feature = "serde")]
#[test]
fn write_update_user_patch_set_all() {
let value = get_update_user_patch_set_all();
let actual: String = serde_json::to_string_pretty(&value).unwrap();
let expected = fs::read_to_string("../../test-resources/core/user/update-user-patch/set-all/value.json").unwrap();
assert_eq!(&actual, expected.trim());
}
#[cfg(feature = "serde")]
fn get_update_user_patch_set_username() -> RawUpdateUserPatch {
RawUpdateUserPatch {
display_name: None,
username: Some(Some("demurgos".parse().unwrap())),
password: None,
}
}
#[cfg(feature = "serde")]
#[test]
fn read_update_user_patch_set_username() {
let s = fs::read_to_string("../../test-resources/core/user/update-user-patch/set-username/value.json").unwrap();
let actual: RawUpdateUserPatch = serde_json::from_str(&s).unwrap();
let expected = get_update_user_patch_set_username();
assert_eq!(actual, expected);
}
#[cfg(feature = "serde")]
#[test]
fn write_update_user_patch_set_username() {
let value = get_update_user_patch_set_username();
let actual: String = serde_json::to_string_pretty(&value).unwrap();
let expected =
fs::read_to_string("../../test-resources/core/user/update-user-patch/set-username/value.json").unwrap();
assert_eq!(&actual, expected.trim());
}
}