dump
This commit is contained in:
commit
bbce466a0e
29 changed files with 1238 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
266
Cargo.lock
generated
Normal file
266
Cargo.lock
generated
Normal file
|
@ -0,0 +1,266 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.197"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.197"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.58",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12dc5c46daa8e9fdf4f5e71b6cf9a53f2487da0e86e55808e2d35539666497dd"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "todos"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"serde",
|
||||
"validator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "validator"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db79c75af171630a3148bd3e6d7c4f42b6a9a014c2945bc5ed0020cbb8d9478e"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"url",
|
||||
]
|
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "todos"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
derive_more = "0.99.17"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
validator = "0.18.1"
|
7
src/main.rs
Normal file
7
src/main.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
//#![allow(dead_code)]
|
||||
|
||||
//mod project;
|
||||
mod user;
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
116
src/project.rs
Normal file
116
src/project.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
use crate::user::User;
|
||||
use derive_more::{Display, Error};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Project {
|
||||
id: usize,
|
||||
name: String,
|
||||
description: Option<String>,
|
||||
members: Vec<User>,
|
||||
}
|
||||
|
||||
pub mod services {
|
||||
use super::*;
|
||||
|
||||
pub fn get_projects_belonging_to_user(
|
||||
project_adapter: &dyn SecondaryPortProject,
|
||||
owner: &User,
|
||||
) -> ProjectResult<Vec<Project>> {
|
||||
project_adapter.get_projects_belonging_to_user(owner.id())
|
||||
}
|
||||
|
||||
pub fn create(
|
||||
project_adapter: &dyn SecondaryPortProject,
|
||||
owner: User,
|
||||
name: String,
|
||||
description: Option<String>,
|
||||
) -> ProjectResult<Project> {
|
||||
let id = project_adapter.create_project(&name, description.as_deref(), owner.id())?;
|
||||
Ok(Project::new(id, name, description, vec![owner]))
|
||||
}
|
||||
|
||||
fn doer_is_member(
|
||||
project_adapter: &dyn SecondaryPortProject,
|
||||
project_id: usize,
|
||||
doer: &User,
|
||||
) -> ProjectResult<bool> {
|
||||
let p = project_adapter.get_project_by_id(project_id)?;
|
||||
Ok(p.doer_is_member(doer))
|
||||
}
|
||||
pub fn update_description(
|
||||
project_adapter: &dyn SecondaryPortProject,
|
||||
project_id: usize,
|
||||
doer: &User,
|
||||
new_description: String,
|
||||
) -> ProjectResult<Project> {
|
||||
if doer_is_member(project_adapter, project_id, doer)? {
|
||||
let mut p = project_adapter.get_project_by_id(project_id)?;
|
||||
project_adapter.update_description(p.id, &new_description)?;
|
||||
p.update_description(new_description);
|
||||
Ok(p)
|
||||
} else {
|
||||
Err(ProjectError::Unauthorized)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Project {
|
||||
// getters
|
||||
pub fn id(&self) -> usize {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn description(&self) -> Option<&str> {
|
||||
self.description.as_deref()
|
||||
}
|
||||
pub fn email(&self) -> &[User] {
|
||||
&self.members
|
||||
}
|
||||
|
||||
fn new(id: usize, name: String, description: Option<String>, members: Vec<User>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
members,
|
||||
}
|
||||
}
|
||||
fn update_description(&mut self, new_description: String) {
|
||||
self.description = Some(new_description)
|
||||
}
|
||||
|
||||
fn doer_is_member(&self, doer: &User) -> bool {
|
||||
self.members.contains(doer)
|
||||
}
|
||||
|
||||
pub fn load(id: usize, name: String, description: Option<String>, members: Vec<User>) -> Self {
|
||||
Self::new(id, name, description, members)
|
||||
}
|
||||
}
|
||||
|
||||
pub type ProjectResult<V> = Result<V, ProjectError>;
|
||||
|
||||
#[derive(Debug, Display, Error)]
|
||||
pub enum ProjectError {
|
||||
Unauthorized,
|
||||
}
|
||||
|
||||
pub trait SecondaryPortProject {
|
||||
// returns ID of newly created Project
|
||||
fn create_project(
|
||||
&self,
|
||||
name: &str,
|
||||
description: Option<&str>,
|
||||
owner_id: usize,
|
||||
) -> ProjectResult<usize>;
|
||||
fn update_description(&self, id: usize, new_description: &str) -> ProjectResult<Project>;
|
||||
|
||||
// get user from DB
|
||||
fn get_project_by_id(&self, id: usize) -> ProjectResult<Project>;
|
||||
fn get_projects_belonging_to_user(&self, user_id: usize) -> ProjectResult<Vec<Project>>;
|
||||
}
|
0
src/user/adapter/in/mod.rs
Normal file
0
src/user/adapter/in/mod.rs
Normal file
1
src/user/adapter/mod.rs
Normal file
1
src/user/adapter/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
|
0
src/user/adapter/out/mod.rs
Normal file
0
src/user/adapter/out/mod.rs
Normal file
37
src/user/application/fetch_user_service/command.rs
Normal file
37
src/user/application/fetch_user_service/command.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::user::domain::UserError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct FetchUserCommand<'a> {
|
||||
login: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> FetchUserCommand<'a> {
|
||||
pub fn login(&self) -> &str {
|
||||
&self.login
|
||||
}
|
||||
|
||||
pub fn new_command(login: &'a str) -> Result<Self, UserError> {
|
||||
let login = login.trim();
|
||||
if login.trim().is_empty() {
|
||||
Err(UserError::LoginIsEmpty)
|
||||
} else {
|
||||
Ok(Self { login })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_fetch_user_cmd() {
|
||||
assert_eq!(
|
||||
FetchUserCommand::new_command(" ",).err(),
|
||||
Some(UserError::LoginIsEmpty)
|
||||
);
|
||||
assert!(FetchUserCommand::new_command("aasdad",).is_ok())
|
||||
}
|
||||
}
|
2
src/user/application/fetch_user_service/mod.rs
Normal file
2
src/user/application/fetch_user_service/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod command;
|
||||
pub mod service;
|
58
src/user/application/fetch_user_service/service.rs
Normal file
58
src/user/application/fetch_user_service/service.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use super::command;
|
||||
use crate::user::application::{port::out::get_user, utils};
|
||||
use crate::user::domain::{User, UserResult};
|
||||
|
||||
use get_user::GetUserPort;
|
||||
|
||||
pub trait FetchUserTrait {
|
||||
fn fetch_user_from_login(&self, cmd: command::FetchUserCommand) -> UserResult<User>;
|
||||
}
|
||||
|
||||
pub struct FetchUserService {
|
||||
get_user: Box<dyn GetUserPort>,
|
||||
}
|
||||
|
||||
impl FetchUserService {
|
||||
pub fn new(get_user: Box<dyn GetUserPort>) -> Self {
|
||||
Self { get_user }
|
||||
}
|
||||
}
|
||||
|
||||
impl FetchUserTrait for FetchUserService {
|
||||
fn fetch_user_from_login(&self, cmd: command::FetchUserCommand) -> UserResult<User> {
|
||||
utils::fetch_user_from_login(self.get_user.as_ref(), cmd.login())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::user::domain::tests::utils::*;
|
||||
|
||||
pub mod utils {
|
||||
use super::*;
|
||||
use crate::user::application::port::out::tests::utils::MockOutPortAdapter;
|
||||
|
||||
impl FetchUserService {
|
||||
pub fn get_fetch_user_service() -> Self {
|
||||
FetchUserService::new(Box::new(MockOutPortAdapter::new()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fetch_user_from_login() {
|
||||
let u = get_user();
|
||||
let s = FetchUserService::get_fetch_user_service();
|
||||
assert_eq!(
|
||||
s.fetch_user_from_login(command::FetchUserCommand::new_command(u.name()).unwrap())
|
||||
.unwrap(),
|
||||
u
|
||||
);
|
||||
assert_eq!(
|
||||
s.fetch_user_from_login(command::FetchUserCommand::new_command(u.email()).unwrap())
|
||||
.unwrap(),
|
||||
u
|
||||
);
|
||||
}
|
||||
}
|
6
src/user/application/mod.rs
Normal file
6
src/user/application/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
mod fetch_user_service;
|
||||
mod port;
|
||||
mod register_service;
|
||||
mod signin_service;
|
||||
mod update_user_password_service;
|
||||
mod utils;
|
1
src/user/application/port/mod.rs
Normal file
1
src/user/application/port/mod.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod out;
|
6
src/user/application/port/out/create_user.rs
Normal file
6
src/user/application/port/out/create_user.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use crate::user::domain::UserResult;
|
||||
|
||||
pub trait CreateUserPort {
|
||||
// returns ID of newly created user user
|
||||
fn create_user(&self, name: &str, password: &str, email: &str) -> UserResult<()>;
|
||||
}
|
8
src/user/application/port/out/get_user.rs
Normal file
8
src/user/application/port/out/get_user.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
use crate::user::domain::{User, UserResult};
|
||||
|
||||
pub trait GetUserPort {
|
||||
// get user from DB
|
||||
fn get_user_by_name(&self, name: &str) -> UserResult<User>;
|
||||
// get user from DB
|
||||
fn get_user_by_email(&self, email: &str) -> UserResult<User>;
|
||||
}
|
84
src/user/application/port/out/mod.rs
Normal file
84
src/user/application/port/out/mod.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
pub(in crate::user::application) mod create_user;
|
||||
pub(in crate::user::application) mod get_user;
|
||||
pub(in crate::user::application) mod update_password;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
pub(crate) mod utils {
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use crate::user::application::port::out::{
|
||||
create_user::CreateUserPort, get_user::GetUserPort, update_password::UpdatePasswordPort,
|
||||
};
|
||||
use crate::user::domain::{tests::utils::*, User, UserResult};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MockOutPortAdapter {
|
||||
users: Arc<RwLock<Vec<User>>>,
|
||||
}
|
||||
|
||||
impl MockOutPortAdapter {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
users: Arc::new(RwLock::new(Vec::default())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_user(&self, name: &str) -> User {
|
||||
let users = self.users.read().unwrap();
|
||||
|
||||
users
|
||||
.get(users.iter().position(|u| u.name() == name).unwrap())
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl GetUserPort for MockOutPortAdapter {
|
||||
fn get_user_by_name(&self, name: &str) -> UserResult<User> {
|
||||
let mut users = self.users.write().unwrap();
|
||||
if let Some(pos) = users.iter().position(|u| u.name() == name) {
|
||||
Ok(users.get(pos).unwrap().to_owned())
|
||||
} else {
|
||||
let u = get_user_by_name(name.to_string());
|
||||
users.push(u.clone());
|
||||
Ok(u)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_user_by_email(&self, email: &str) -> UserResult<User> {
|
||||
let mut users = self.users.write().unwrap();
|
||||
if let Some(pos) = users.iter().position(|u| u.email() == email) {
|
||||
Ok(users.get(pos).unwrap().to_owned())
|
||||
} else {
|
||||
let u = get_user_by_email(email.to_string());
|
||||
users.push(u.clone());
|
||||
Ok(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UpdatePasswordPort for MockOutPortAdapter {
|
||||
fn update_password(&self, id: usize, new_password: &str) -> UserResult<()> {
|
||||
let mut users = self.users.write().unwrap();
|
||||
if let Some(pos) = users.iter().position(|u| u.id() == id) {
|
||||
let u = users.get_mut(pos).unwrap();
|
||||
u.set_password(new_password.to_owned());
|
||||
} else {
|
||||
let mut u = get_user_by_id(id);
|
||||
u.set_password(new_password.to_owned());
|
||||
users.push(u);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl CreateUserPort for MockOutPortAdapter {
|
||||
fn create_user(&self, name: &str, password: &str, email: &str) -> UserResult<()> {
|
||||
let u = User::new(1, name.into(), password.into(), email.into());
|
||||
self.users.write().unwrap().push(u);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
src/user/application/port/out/update_password.rs
Normal file
5
src/user/application/port/out/update_password.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
use crate::user::domain::UserResult;
|
||||
|
||||
pub trait UpdatePasswordPort {
|
||||
fn update_password(&self, id: usize, new_password: &str) -> UserResult<()>;
|
||||
}
|
114
src/user/application/register_service/command.rs
Normal file
114
src/user/application/register_service/command.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use validator::ValidateEmail;
|
||||
|
||||
use crate::user::domain::UserError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct RegisterUserCommand {
|
||||
name: String,
|
||||
password: String,
|
||||
email: String,
|
||||
}
|
||||
|
||||
impl RegisterUserCommand {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn password(&self) -> &str {
|
||||
&self.password
|
||||
}
|
||||
pub fn email(&self) -> &str {
|
||||
&self.email
|
||||
}
|
||||
|
||||
pub fn new_command(
|
||||
name: String,
|
||||
password: String,
|
||||
confirm_password: String,
|
||||
email: String,
|
||||
) -> Result<Self, UserError> {
|
||||
let name = name.trim().to_owned();
|
||||
let email = email.trim().to_owned();
|
||||
|
||||
if name.is_empty() {
|
||||
return Err(UserError::UsernameIsEmpty);
|
||||
}
|
||||
|
||||
if confirm_password != password {
|
||||
return Err(UserError::PasswordsDontMatch);
|
||||
}
|
||||
|
||||
if !email.validate_email() {
|
||||
return Err(UserError::NotAnEmail);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
name,
|
||||
password,
|
||||
email,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::user::domain::tests::utils::*;
|
||||
|
||||
#[test]
|
||||
fn test_register_cmd() {
|
||||
let u = get_user();
|
||||
|
||||
assert_eq!(
|
||||
RegisterUserCommand::new_command(
|
||||
" ".to_string(),
|
||||
u.password().to_string(),
|
||||
u.password().to_string(),
|
||||
u.email().to_string(),
|
||||
)
|
||||
.err(),
|
||||
Some(UserError::UsernameIsEmpty)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
RegisterUserCommand::new_command(
|
||||
u.name().to_string(),
|
||||
u.password().to_string(),
|
||||
u.name().to_string(),
|
||||
u.email().to_string(),
|
||||
)
|
||||
.err(),
|
||||
Some(UserError::PasswordsDontMatch)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
RegisterUserCommand::new_command(
|
||||
u.name().to_string(),
|
||||
u.password().to_string(),
|
||||
u.password().to_string(),
|
||||
u.password().to_string(),
|
||||
)
|
||||
.err(),
|
||||
Some(UserError::NotAnEmail)
|
||||
);
|
||||
|
||||
let _ = RegisterUserCommand::get_command();
|
||||
}
|
||||
|
||||
pub(crate) mod utils {
|
||||
use super::*;
|
||||
impl RegisterUserCommand {
|
||||
pub(crate) fn get_command() -> Self {
|
||||
let u = get_user();
|
||||
RegisterUserCommand::new_command(
|
||||
u.name().to_string(),
|
||||
u.password().to_string(),
|
||||
u.password().to_string(),
|
||||
u.email().to_string(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
src/user/application/register_service/mod.rs
Normal file
2
src/user/application/register_service/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod command;
|
||||
pub mod service;
|
63
src/user/application/register_service/service.rs
Normal file
63
src/user/application/register_service/service.rs
Normal file
|
@ -0,0 +1,63 @@
|
|||
use crate::user::application::port::out::create_user;
|
||||
use crate::user::domain::UserResult;
|
||||
|
||||
use super::command;
|
||||
|
||||
pub trait RegisterUserTrait {
|
||||
fn register(&self, cmd: command::RegisterUserCommand) -> UserResult<()>;
|
||||
}
|
||||
|
||||
pub struct RegisterUserService {
|
||||
crate_user_adapter: Box<dyn create_user::CreateUserPort>,
|
||||
}
|
||||
|
||||
impl RegisterUserService {
|
||||
pub fn new(crate_user_adapter: Box<dyn create_user::CreateUserPort>) -> Self {
|
||||
Self { crate_user_adapter }
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterUserTrait for RegisterUserService {
|
||||
fn register(&self, cmd: command::RegisterUserCommand) -> UserResult<()> {
|
||||
self.crate_user_adapter
|
||||
.create_user(cmd.name(), cmd.password(), cmd.email())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use tests::command::RegisterUserCommand;
|
||||
|
||||
use super::*;
|
||||
use crate::user::domain::tests::utils::*;
|
||||
|
||||
pub mod utils {
|
||||
use super::*;
|
||||
use crate::user::application::port::out::tests::utils::MockOutPortAdapter;
|
||||
|
||||
impl RegisterUserService {
|
||||
pub fn get_register_service() -> (Self, MockOutPortAdapter) {
|
||||
let adapter = MockOutPortAdapter::new();
|
||||
(RegisterUserService::new(Box::new(adapter.clone())), adapter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_register_service() {
|
||||
let u = get_user();
|
||||
|
||||
let (s, adapter) = RegisterUserService::get_register_service();
|
||||
let cmd = RegisterUserCommand::get_command();
|
||||
|
||||
s.register(cmd).unwrap();
|
||||
let new_user = adapter.get_user(u.name());
|
||||
assert_eq!(new_user, u);
|
||||
assert_ne!(
|
||||
new_user.id(),
|
||||
0,
|
||||
"default ID used to create user before getting ID assigned by DB"
|
||||
)
|
||||
}
|
||||
}
|
49
src/user/application/signin_service/command.rs
Normal file
49
src/user/application/signin_service/command.rs
Normal file
|
@ -0,0 +1,49 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::user::domain::UserError;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct SigninUserCommand<'a> {
|
||||
login: &'a str,
|
||||
password: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> SigninUserCommand<'a> {
|
||||
pub fn password(&self) -> &str {
|
||||
&self.password
|
||||
}
|
||||
|
||||
pub fn login(&self) -> &str {
|
||||
&self.login
|
||||
}
|
||||
|
||||
pub fn new_command(login: &'a str, password: &'a str) -> Result<Self, UserError> {
|
||||
let login = login.trim();
|
||||
if password.is_empty() {
|
||||
Err(UserError::PasswordIsEmpty)
|
||||
} else if login.is_empty() {
|
||||
Err(UserError::LoginIsEmpty)
|
||||
} else {
|
||||
Ok(Self { password, login })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_sign_user_cmd() {
|
||||
assert_eq!(
|
||||
SigninUserCommand::new_command("foo", "",).err(),
|
||||
Some(UserError::PasswordIsEmpty)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
SigninUserCommand::new_command(" ", "foo",).err(),
|
||||
Some(UserError::LoginIsEmpty)
|
||||
);
|
||||
assert!(SigninUserCommand::new_command("foo", "foo",).is_ok(),);
|
||||
}
|
||||
}
|
2
src/user/application/signin_service/mod.rs
Normal file
2
src/user/application/signin_service/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod command;
|
||||
pub mod service;
|
74
src/user/application/signin_service/service.rs
Normal file
74
src/user/application/signin_service/service.rs
Normal file
|
@ -0,0 +1,74 @@
|
|||
use super::command;
|
||||
use crate::user::application::{port::out::get_user, utils};
|
||||
use crate::user::domain::{UserError, UserResult};
|
||||
use get_user::GetUserPort;
|
||||
|
||||
pub trait SigninUserTrait {
|
||||
fn signin(&self, cmd: command::SigninUserCommand) -> UserResult<()>;
|
||||
}
|
||||
|
||||
pub struct SigninUserService {
|
||||
get_user: Box<dyn GetUserPort>,
|
||||
}
|
||||
|
||||
impl SigninUserService {
|
||||
pub fn new(get_user: Box<dyn GetUserPort>) -> Self {
|
||||
Self { get_user }
|
||||
}
|
||||
}
|
||||
|
||||
impl SigninUserTrait for SigninUserService {
|
||||
fn signin(&self, cmd: command::SigninUserCommand) -> UserResult<()> {
|
||||
let u = utils::fetch_user_from_login(self.get_user.as_ref(), cmd.login())?;
|
||||
if u.password() == cmd.password() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(UserError::WrongPassword)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::user::domain::tests::utils::*;
|
||||
use crate::user::domain::UserError;
|
||||
|
||||
pub mod utils {
|
||||
use crate::user::application::port::out::tests::utils::MockOutPortAdapter;
|
||||
|
||||
use super::*;
|
||||
impl SigninUserService {
|
||||
pub fn get_signin_user_service() -> Self {
|
||||
SigninUserService::new(Box::new(MockOutPortAdapter::new()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signin_service() {
|
||||
let u = get_user();
|
||||
let s = SigninUserService::get_signin_user_service();
|
||||
|
||||
assert_eq!(
|
||||
s.signin(command::SigninUserCommand::new_command(u.name(), u.name()).unwrap())
|
||||
.err(),
|
||||
Some(UserError::WrongPassword)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
s.signin(command::SigninUserCommand::new_command(u.email(), u.name()).unwrap())
|
||||
.err(),
|
||||
Some(UserError::WrongPassword)
|
||||
);
|
||||
|
||||
assert!(s
|
||||
.signin(command::SigninUserCommand::new_command(u.email(), u.password()).unwrap())
|
||||
.is_ok());
|
||||
|
||||
assert!(s
|
||||
.signin(command::SigninUserCommand::new_command(u.name(), u.password()).unwrap())
|
||||
.is_ok());
|
||||
}
|
||||
}
|
77
src/user/application/update_user_password_service/command.rs
Normal file
77
src/user/application/update_user_password_service/command.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use crate::user::domain::UserError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct UpdateUserPasswordCommand<'a> {
|
||||
login: &'a str,
|
||||
old_password: &'a str,
|
||||
new_password: String,
|
||||
}
|
||||
|
||||
impl<'a> UpdateUserPasswordCommand<'a> {
|
||||
pub fn login(&self) -> &str {
|
||||
&self.login
|
||||
}
|
||||
|
||||
pub fn new_password(&self) -> &str {
|
||||
&self.new_password
|
||||
}
|
||||
|
||||
pub fn old_password(&self) -> &str {
|
||||
self.old_password
|
||||
}
|
||||
|
||||
pub fn new_command(
|
||||
login: &'a str,
|
||||
old_password: &'a str,
|
||||
new_password: String,
|
||||
new_password_confirm: String,
|
||||
) -> Result<Self, UserError> {
|
||||
let login = login.trim();
|
||||
|
||||
if login.is_empty() {
|
||||
return Err(UserError::LoginIsEmpty);
|
||||
}
|
||||
|
||||
if new_password != new_password_confirm {
|
||||
return Err(UserError::PasswordsDontMatch);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
login,
|
||||
old_password,
|
||||
new_password,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::user::domain::UserError;
|
||||
|
||||
#[test]
|
||||
fn test_update_password_command() {
|
||||
assert_eq!(
|
||||
UpdateUserPasswordCommand::new_command(
|
||||
" ",
|
||||
"password",
|
||||
"new_password".to_string(),
|
||||
"new_password".to_string(),
|
||||
)
|
||||
.err(),
|
||||
Some(UserError::LoginIsEmpty)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
UpdateUserPasswordCommand::new_command(
|
||||
"user",
|
||||
"password",
|
||||
"new_password1".to_string(),
|
||||
"new_password".to_string()
|
||||
)
|
||||
.err(),
|
||||
Some(UserError::PasswordsDontMatch)
|
||||
);
|
||||
}
|
||||
}
|
2
src/user/application/update_user_password_service/mod.rs
Normal file
2
src/user/application/update_user_password_service/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod command;
|
||||
pub mod service;
|
114
src/user/application/update_user_password_service/service.rs
Normal file
114
src/user/application/update_user_password_service/service.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
use super::command;
|
||||
use crate::user::application::{
|
||||
port::out::{get_user::GetUserPort, update_password::UpdatePasswordPort},
|
||||
utils,
|
||||
};
|
||||
use crate::user::domain::{UserError, UserResult};
|
||||
|
||||
pub trait UpdateUserPasswordTrait {
|
||||
fn update_password(&self, cmd: command::UpdateUserPasswordCommand) -> UserResult<()>;
|
||||
}
|
||||
|
||||
pub struct UpdateUserPasswordService {
|
||||
get_user: Box<dyn GetUserPort>,
|
||||
update_password: Box<dyn UpdatePasswordPort>,
|
||||
}
|
||||
|
||||
impl UpdateUserPasswordService {
|
||||
pub fn new(
|
||||
get_user: Box<dyn GetUserPort>,
|
||||
update_password: Box<dyn UpdatePasswordPort>,
|
||||
) -> Self {
|
||||
Self {
|
||||
get_user,
|
||||
update_password,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UpdateUserPasswordTrait for UpdateUserPasswordService {
|
||||
fn update_password(&self, cmd: command::UpdateUserPasswordCommand) -> UserResult<()> {
|
||||
let mut u = utils::fetch_user_from_login(self.get_user.as_ref(), cmd.login())?;
|
||||
|
||||
if u.password() != cmd.old_password() {
|
||||
return Err(UserError::WrongPassword);
|
||||
}
|
||||
|
||||
if u.password() == cmd.new_password() {
|
||||
return Err(UserError::PasswordAlreadyUsed);
|
||||
}
|
||||
|
||||
u.set_password(cmd.new_password().to_owned());
|
||||
self.update_password.update_password(u.id(), u.password())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::user::domain::tests::utils::*;
|
||||
use crate::user::domain::UserError;
|
||||
|
||||
pub mod utils {
|
||||
use crate::user::application::port::out::tests::utils::MockOutPortAdapter;
|
||||
|
||||
use super::*;
|
||||
impl UpdateUserPasswordService {
|
||||
pub fn get_update_user_password_service() -> Self {
|
||||
let a = MockOutPortAdapter::new();
|
||||
UpdateUserPasswordService::new(Box::new(a.clone()), Box::new(a.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update_password_service() {
|
||||
let u = get_user();
|
||||
let s = UpdateUserPasswordService::get_update_user_password_service();
|
||||
let new_passowrd = "newpassword";
|
||||
|
||||
assert_eq!(
|
||||
s.update_password(
|
||||
command::UpdateUserPasswordCommand::new_command(
|
||||
u.name(),
|
||||
"wrong_password",
|
||||
new_passowrd.into(),
|
||||
new_passowrd.into()
|
||||
)
|
||||
.unwrap()
|
||||
)
|
||||
.err(),
|
||||
Some(UserError::WrongPassword)
|
||||
);
|
||||
let old_password = u.password().to_owned();
|
||||
|
||||
assert_eq!(
|
||||
s.update_password(
|
||||
command::UpdateUserPasswordCommand::new_command(
|
||||
u.name(),
|
||||
&old_password,
|
||||
old_password.clone(),
|
||||
old_password.clone(),
|
||||
)
|
||||
.unwrap()
|
||||
)
|
||||
.err(),
|
||||
Some(UserError::PasswordAlreadyUsed)
|
||||
);
|
||||
s.update_password(
|
||||
command::UpdateUserPasswordCommand::new_command(
|
||||
u.name(),
|
||||
&old_password,
|
||||
new_passowrd.into(),
|
||||
new_passowrd.into(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let updated_user =
|
||||
super::utils::fetch_user_from_login(s.get_user.as_ref(), u.name()).unwrap();
|
||||
assert_ne!(updated_user.password(), u.password());
|
||||
assert_eq!(updated_user.password(), new_passowrd);
|
||||
}
|
||||
}
|
36
src/user/application/utils.rs
Normal file
36
src/user/application/utils.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use validator::ValidateEmail;
|
||||
|
||||
use super::port::out::get_user;
|
||||
use crate::user::domain::{User, UserResult};
|
||||
use get_user::*;
|
||||
|
||||
pub(super) fn fetch_user_from_login(get_user: &dyn GetUserPort, login: &str) -> UserResult<User> {
|
||||
if login.validate_email() {
|
||||
get_user.get_user_by_email(login)
|
||||
} else {
|
||||
get_user.get_user_by_name(login)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::user::domain::tests::utils::*;
|
||||
|
||||
use crate::user::application::port::out::tests::utils::MockOutPortAdapter;
|
||||
|
||||
#[test]
|
||||
fn test_fetch_user_from_login() {
|
||||
let u = get_user();
|
||||
let adapter: Box<dyn GetUserPort> = Box::new(MockOutPortAdapter::new());
|
||||
|
||||
assert_eq!(
|
||||
fetch_user_from_login(adapter.as_ref(), u.name()).unwrap(),
|
||||
u
|
||||
);
|
||||
assert_eq!(
|
||||
fetch_user_from_login(adapter.as_ref(), u.email()).unwrap(),
|
||||
u
|
||||
);
|
||||
}
|
||||
}
|
93
src/user/domain/mod.rs
Normal file
93
src/user/domain/mod.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
use derive_more::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct User {
|
||||
id: usize,
|
||||
name: String,
|
||||
password: String,
|
||||
email: String,
|
||||
}
|
||||
|
||||
impl User {
|
||||
// getters
|
||||
pub fn id(&self) -> usize {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn password(&self) -> &str {
|
||||
&self.password
|
||||
}
|
||||
pub fn email(&self) -> &str {
|
||||
&self.email
|
||||
}
|
||||
|
||||
pub(crate) fn set_password(&mut self, password: String) {
|
||||
self.password = password;
|
||||
}
|
||||
|
||||
pub(crate) fn new(id: usize, name: String, password: String, email: String) -> Self {
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
password,
|
||||
email,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type UserResult<V> = Result<V, UserError>;
|
||||
|
||||
#[derive(Debug, Display, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum UserError {
|
||||
DuplicateUsername,
|
||||
DuplicateEmail,
|
||||
InternalError,
|
||||
PasswordsDontMatch,
|
||||
PasswordAlreadyUsed,
|
||||
PasswordIsEmpty,
|
||||
WrongPassword,
|
||||
NotAnEmail,
|
||||
UsernameIsEmpty,
|
||||
LoginIsEmpty,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
|
||||
pub(crate) mod utils {
|
||||
use super::*;
|
||||
|
||||
pub fn get_user() -> User {
|
||||
let password = "password";
|
||||
let email = "user1@foo.com";
|
||||
let name = "user1";
|
||||
|
||||
User::new(1, name.into(), password.into(), email.into())
|
||||
}
|
||||
|
||||
pub fn get_user_by_id(id: usize) -> User {
|
||||
let mut u = get_user();
|
||||
|
||||
u.id = id;
|
||||
u
|
||||
}
|
||||
|
||||
pub fn get_user_by_name(name: String) -> User {
|
||||
let mut u = get_user();
|
||||
u.name = name;
|
||||
u
|
||||
}
|
||||
|
||||
pub fn get_user_by_email(email: String) -> User {
|
||||
let mut u = get_user();
|
||||
u.email = email;
|
||||
u
|
||||
}
|
||||
}
|
||||
}
|
3
src/user/mod.rs
Normal file
3
src/user/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod adapter;
|
||||
pub mod application;
|
||||
pub mod domain;
|
Reference in a new issue