From 5cff261d4beff9a25eab16fe7b24fde5293c3fe8 Mon Sep 17 00:00:00 2001 From: Anton Bilous Date: Thu, 30 Jan 2025 20:37:59 +0200 Subject: [PATCH] Simplify CRUD trait & Package repository --- src/database/src/adapter/mysql.rs | 3 +- src/database/src/adapter/mysql/base.rs | 93 +++++----- src/database/src/adapter/mysql/package.rs | 150 +++++++++++++++ src/database/src/adapter/mysql/user.rs | 115 +++++++----- src/database/src/lib.rs | 2 +- src/database/src/main.rs | 18 -- src/database/src/port.rs | 23 +-- src/database/src/port/base.rs | 61 +++--- src/database/src/port/package.rs | 216 +++++++--------------- src/database/src/port/user.rs | 72 +++----- 10 files changed, 390 insertions(+), 363 deletions(-) create mode 100644 src/database/src/adapter/mysql/package.rs delete mode 100644 src/database/src/main.rs diff --git a/src/database/src/adapter/mysql.rs b/src/database/src/adapter/mysql.rs index 5e21c1d..ec9df17 100644 --- a/src/database/src/adapter/mysql.rs +++ b/src/database/src/adapter/mysql.rs @@ -1,2 +1,3 @@ -pub mod user; pub mod base; +pub mod package; +pub mod user; diff --git a/src/database/src/adapter/mysql/base.rs b/src/database/src/adapter/mysql/base.rs index 559c6e3..347013e 100644 --- a/src/database/src/adapter/mysql/base.rs +++ b/src/database/src/adapter/mysql/base.rs @@ -4,95 +4,94 @@ use sqlx::{Executor, MySql}; pub struct BaseAdapter; -struct DatabaseBase { - id: u64, - name: String, - description: Option, - created_at: DateTime, - updated_at: DateTime, -} -impl From for Base { - fn from(value: DatabaseBase) -> Self { - Self { - id: Id(value.id), - name: value.name.into(), - description: value.description.into(), - created_at: value.created_at, - updated_at: value.updated_at, - } - } -} - impl BaseRepository for BaseAdapter where for<'a> &'a E: Executor<'a, Database = MySql> {} impl crate::port::CRUD for BaseAdapter where for<'a> &'a E: Executor<'a, Database = MySql>, { - type Create = New; - type Read = Id; + type New = New; + type Unique = u64; type Update = Field; - type Delete = Id; type Existing = Base; - type Id = Id; - async fn create(connection: &mut E, data: Self::Create) -> Result { - Ok(Id(sqlx::query!( - "INSERT INTO PackageBases (name, description) VALUES (?, ?)", + async fn create(connection: &mut E, data: Self::New) -> Result { + let created_at = Utc::now(); + let id = sqlx::query!( + "INSERT INTO PackageBases (name, description, created_at, updated_at) VALUES (?, ?, ?, ?)", data.name.0, data.description.0, + created_at, created_at, ) .execute(&*connection) .await? - .last_insert_id())) + .last_insert_id(); + + Ok(Self::Existing { + id, + name: data.name.into_inner().0, + description: data.description.into_inner().0, + created_at, + updated_at: created_at, + }) } - async fn read(connection: &E, data: Self::Read) -> Result> { - Ok(sqlx::query_as!( - DatabaseBase, - "SELECT * FROM PackageBases WHERE id = ?", - data.0 + async fn read(connection: &E, data: Self::Unique) -> Result> { + Ok( + sqlx::query_as!(Base, "SELECT * FROM PackageBases WHERE id = ?", data) + .fetch_optional(connection) + .await?, ) - .fetch_optional(connection) - .await? - .map(Into::into)) } - async fn update(connection: &mut E, id: Self::Id, data: Self::Update) -> Result { - match data { - Field::Name(valid) => { + async fn update( + connection: &mut E, + existing: &mut Self::Existing, + data: Self::Update, + ) -> Result { + match &data { + Field::Name(name) => { sqlx::query!( "UPDATE PackageBases SET name = ? WHERE id = ?", - valid.0, - id.0 + name.0, + existing.id ) } - Field::Description(valid) => { + Field::Description(description) => { sqlx::query!( "UPDATE PackageBases SET description = ? WHERE id = ?", - valid.0, - id.0 + description.0, + existing.id ) } Field::CreatedAt(date_time) => sqlx::query!( "UPDATE PackageBases SET created_at = ? WHERE id = ?", date_time, - id.0 + existing.id ), Field::UpdatedAt(date_time) => sqlx::query!( "UPDATE PackageBases SET updated_at = ? WHERE id = ?", date_time, - id.0 + existing.id ), } .execute(&*connection) .await?; + + match data { + Field::Name(valid) => existing.name = valid.into_inner().0, + Field::Description(valid) => existing.description = valid.into_inner().0, + Field::CreatedAt(date_time) => existing.created_at = date_time, + Field::UpdatedAt(date_time) => existing.updated_at = date_time, + } + Ok(()) } - async fn delete(connection: &mut E, data: Self::Delete) -> Result { - sqlx::query!("DELETE FROM PackageBases WHERE id = ?", data.0) + async fn delete(connection: &mut E, data: Self::Unique) -> Result { + sqlx::query!("DELETE FROM PackageBases WHERE id = ?", data) .execute(&*connection) .await?; + Ok(()) } } diff --git a/src/database/src/adapter/mysql/package.rs b/src/database/src/adapter/mysql/package.rs new file mode 100644 index 0000000..7cb483d --- /dev/null +++ b/src/database/src/adapter/mysql/package.rs @@ -0,0 +1,150 @@ +pub use crate::port::package::*; + +use sqlx::{Executor, MySql}; + +pub struct PackageAdapter; + +impl PackageRepository for PackageAdapter where for<'a> &'a E: Executor<'a, Database = MySql> {} +impl crate::port::CRUD for PackageAdapter +where + for<'a> &'a E: Executor<'a, Database = MySql>, +{ + type New = New; + type Update = Field; + type Unique = Unique; + type Existing = Package; + + async fn create(connection: &mut E, data: Self::New) -> Result { + let created_at = Utc::now(); + let id = sqlx::query!( + "INSERT INTO Packages \ + (package_base, name, version, description, url, flagged_at, created_at, updated_at) \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + data.package_base.id, + data.name.0, + data.version.0, + data.description.0, + data.url.0, + data.flagged_at, + created_at, + created_at, + ) + .execute(&*connection) + .await? + .last_insert_id(); + + Ok(Self::Existing { + id, + package_base: data.package_base.id, + name: data.name.into_inner().0, + version: data.version.into_inner().0, + description: data.description.into_inner().0, + url: data.url.into_inner().0, + flagged_at: data.flagged_at, + created_at, + updated_at: created_at, + }) + } + + async fn read(connection: &E, data: Self::Unique) -> Result> { + Ok(match data { + Unique::Id(id) => { + sqlx::query_as!(Package, "SELECT * FROM Packages WHERE id = ?", id) + .fetch_optional(connection) + .await + } + Unique::Name(name) => { + sqlx::query_as!(Package, "SELECT * FROM Packages WHERE name = ?", name.0) + .fetch_optional(connection) + .await + } + }?) + } + + async fn update( + connection: &mut E, + existing: &mut Self::Existing, + data: Self::Update, + ) -> Result { + match &data { + Field::Name(name) => { + sqlx::query!( + "UPDATE Packages SET name = ? WHERE id = ?", + name.0, + existing.id + ) + } + Field::PackageBase(package_base) => { + sqlx::query!( + "UPDATE Packages SET package_base = ? WHERE id = ?", + package_base.id, + existing.id + ) + } + Field::Version(version) => { + sqlx::query!( + "UPDATE Packages SET version = ? WHERE id = ?", + version.0, + existing.id + ) + } + Field::Description(description) => { + sqlx::query!( + "UPDATE Packages SET description = ? WHERE id = ?", + description.0, + existing.id + ) + } + Field::URL(url) => { + sqlx::query!( + "UPDATE Packages SET url = ? WHERE id = ?", + url.0, + existing.id + ) + } + Field::FlaggedAt(date_time) => sqlx::query!( + "UPDATE Packages SET flagged_at = ? WHERE id = ?", + date_time, + existing.id + ), + Field::CreatedAt(date_time) => sqlx::query!( + "UPDATE Packages SET created_at = ? WHERE id = ?", + date_time, + existing.id + ), + Field::UpdatedAt(date_time) => sqlx::query!( + "UPDATE Packages SET updated_at = ? WHERE id = ?", + date_time, + existing.id + ), + } + .execute(&*connection) + .await?; + + match data { + Field::Name(valid) => existing.name = valid.into_inner().0, + Field::PackageBase(base) => existing.package_base = base.id, + Field::Version(valid) => existing.version = valid.into_inner().0, + Field::Description(valid) => existing.description = valid.into_inner().0, + Field::URL(valid) => existing.url = valid.into_inner().0, + Field::FlaggedAt(date_time) => existing.flagged_at = date_time, + Field::CreatedAt(date_time) => existing.created_at = date_time, + Field::UpdatedAt(date_time) => existing.updated_at = date_time, + } + + Ok(()) + } + + async fn delete(connection: &mut E, data: Self::Unique) -> Result { + match data { + Unique::Id(id) => sqlx::query!("DELETE FROM Packages WHERE id = ?", id), + Unique::Name(name) => { + sqlx::query!("DELETE FROM Packages WHERE name = ?", name.0) + } + } + .execute(&*connection) + .await?; + + Ok(()) + } +} diff --git a/src/database/src/adapter/mysql/user.rs b/src/database/src/adapter/mysql/user.rs index 0dada5f..60d0df3 100644 --- a/src/database/src/adapter/mysql/user.rs +++ b/src/database/src/adapter/mysql/user.rs @@ -4,113 +4,125 @@ use sqlx::{Executor, MySql}; pub struct UserAdapter; -struct DatabaseUser { - id: u64, - name: String, - email: String, - password: String, - last_used: Option>, - created_at: DateTime, - updated_at: DateTime, -} - -impl From for User { - fn from(value: DatabaseUser) -> Self { - Self { - id: Id(value.id), - name: value.name.into(), - email: value.email.into(), - password: value.password.into(), - last_used: value.last_used, - created_at: value.created_at, - updated_at: value.updated_at, - } - } -} - impl UserRepository for UserAdapter where for<'a> &'a E: Executor<'a, Database = MySql> {} impl crate::port::CRUD for UserAdapter where for<'a> &'a E: Executor<'a, Database = MySql>, { - type Create = New; - type Read = Unique; + type New = New; type Update = Field; - type Delete = Unique; + type Unique = Unique; type Existing = User; - type Id = Id; - async fn create(connection: &mut E, data: Self::Create) -> Result { - Ok(Id(sqlx::query!( - "INSERT INTO Users (name, email, password, last_used) VALUES (?, ?, ?, ?)", + async fn create(connection: &mut E, data: Self::New) -> Result { + let created_at = Utc::now(); + let id = sqlx::query!( + "INSERT INTO Users (name, email, password, last_used, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)", data.name.0, data.email.0, data.password.0, data.last_used, + created_at, + created_at, ) .execute(&*connection) .await? - .last_insert_id())) + .last_insert_id(); + + Ok(Self::Existing { + id, + name: data.name.into_inner().0, + email: data.email.into_inner().0, + password: data.password.into_inner().0, + last_used: data.last_used, + created_at, + updated_at: created_at, + }) } - async fn read(connection: &E, data: Self::Read) -> Result> { + async fn read(connection: &E, data: Self::Unique) -> Result> { Ok(match data { Unique::Id(id) => { - sqlx::query_as!(DatabaseUser, "SELECT * FROM Users WHERE id = ?", id.0) + sqlx::query_as!(User, "SELECT * FROM Users WHERE id = ?", id) .fetch_optional(connection) .await } Unique::Name(name) => { - sqlx::query_as!(DatabaseUser, "SELECT * FROM Users WHERE name = ?", name.0) + sqlx::query_as!(User, "SELECT * FROM Users WHERE name = ?", name.0) .fetch_optional(connection) .await } Unique::Email(email) => { - sqlx::query_as!(DatabaseUser, "SELECT * FROM Users WHERE email = ?", email.0) + sqlx::query_as!(User, "SELECT * FROM Users WHERE email = ?", email.0) .fetch_optional(connection) .await } - }? - .map(Into::into)) + }?) } - async fn update(connection: &mut E, id: Self::Id, data: Self::Update) -> Result { - match data { - Field::Name(valid) => { - sqlx::query!("UPDATE Users SET name = ? WHERE id = ?", valid.0, id.0) + async fn update( + connection: &mut E, + existing: &mut Self::Existing, + data: Self::Update, + ) -> Result { + match &data { + Field::Name(name) => { + sqlx::query!( + "UPDATE Users SET name = ? WHERE id = ?", + name.0, + existing.id + ) } - Field::Email(valid) => { - sqlx::query!("UPDATE Users SET email = ? WHERE id = ?", valid.0, id.0) + Field::Email(email) => { + sqlx::query!( + "UPDATE Users SET email = ? WHERE id = ?", + email.0, + existing.id + ) } - Field::Password(valid) => { - sqlx::query!("UPDATE Users SET password = ? WHERE id = ?", valid.0, id.0) + Field::Password(password) => { + sqlx::query!( + "UPDATE Users SET password = ? WHERE id = ?", + password.0, + existing.id + ) } Field::LastUsed(date_time) => { sqlx::query!( "UPDATE Users SET last_used = ? WHERE id = ?", date_time, - id.0 + existing.id ) } Field::CreatedAt(date_time) => sqlx::query!( "UPDATE Users SET created_at = ? WHERE id = ?", date_time, - id.0 + existing.id ), Field::UpdatedAt(date_time) => sqlx::query!( "UPDATE Users SET updated_at = ? WHERE id = ?", date_time, - id.0 + existing.id ), } .execute(&*connection) .await?; + + match data { + Field::Name(valid) => existing.name = valid.into_inner().0, + Field::Email(valid) => existing.email = valid.into_inner().0, + Field::Password(valid) => existing.password = valid.into_inner().0, + Field::LastUsed(date_time) => existing.last_used = date_time, + Field::CreatedAt(date_time) => existing.created_at = date_time, + Field::UpdatedAt(date_time) => existing.updated_at = date_time, + } + Ok(()) } - async fn delete(connection: &mut E, data: Self::Delete) -> Result { + async fn delete(connection: &mut E, data: Self::Unique) -> Result { match data { - Unique::Id(id) => sqlx::query!("DELETE FROM Users WHERE id = ?", id.0), + Unique::Id(id) => sqlx::query!("DELETE FROM Users WHERE id = ?", id), Unique::Name(name) => { sqlx::query!("DELETE FROM Users WHERE name = ?", name.0) } @@ -120,6 +132,7 @@ where } .execute(&*connection) .await?; + Ok(()) } } diff --git a/src/database/src/lib.rs b/src/database/src/lib.rs index da8cf3a..86d563e 100644 --- a/src/database/src/lib.rs +++ b/src/database/src/lib.rs @@ -11,7 +11,7 @@ pub trait IntoValid: Validate { } impl IntoValid for T {} +pub mod adapter; pub mod atomic; pub mod connect; pub mod port; -pub mod adapter; diff --git a/src/database/src/main.rs b/src/database/src/main.rs deleted file mode 100644 index eb43d13..0000000 --- a/src/database/src/main.rs +++ /dev/null @@ -1,18 +0,0 @@ -use garde::{Valid, Validate}; - -#[derive(Validate)] -enum Data { - Struct { - #[garde(range(min=-10, max=10))] - field: i32, - }, - Tuple(#[garde(ascii)] String), -} - -fn main() { - let data = Data::Struct { field: 100 }; - let data = Data::Tuple("🧱".into()); - if let Err(e) = data.validate() { - println!("invalid data: {e}"); - } -} diff --git a/src/database/src/port.rs b/src/database/src/port.rs index c0b5d0c..d2e13cf 100644 --- a/src/database/src/port.rs +++ b/src/database/src/port.rs @@ -2,20 +2,21 @@ pub type Result = std::result::Result>; #[allow(async_fn_in_trait)] pub trait CRUD { - type Create; - type Read; + type New; + type Unique; type Update; - type Delete; type Existing; - type Id; - async fn create(connection: &mut C, data: Self::Create) -> Result; - async fn read(connection: &C, data: Self::Read) -> Result>; - async fn update(connection: &mut C, id: Self::Id, data: Self::Update) -> Result; - async fn delete(connection: &mut C, data: Self::Delete) -> Result; + async fn create(connection: &mut C, data: Self::New) -> Result; + async fn read(connection: &C, data: Self::Unique) -> Result>; + async fn update( + connection: &mut C, + existing: &mut Self::Existing, + data: Self::Update, + ) -> Result; + async fn delete(connection: &mut C, data: Self::Unique) -> Result; } -pub mod user; pub mod base; -// pub mod package; -// pub mod session; +pub mod package; +pub mod user; diff --git a/src/database/src/port/base.rs b/src/database/src/port/base.rs index f241124..d3fe2bc 100644 --- a/src/database/src/port/base.rs +++ b/src/database/src/port/base.rs @@ -6,45 +6,21 @@ use garde::{Valid, Validate}; #[allow(async_fn_in_trait)] pub trait BaseRepository: - super::CRUD< - C, - Create = New, - Read = Id, - Update = Field, - Delete = Id, - Existing = Base, - Id = Id, - > + CRUD { - async fn update_base(connection: &mut C, base: &mut Base, data: Self::Update) -> Result { - Self::update(connection, base.id, data.clone()).await?; - match data { - Field::Name(valid) => base.name = valid.into_inner(), - Field::Description(valid) => base.description = valid.into_inner(), - Field::CreatedAt(date_time) => base.created_at = date_time, - Field::UpdatedAt(date_time) => base.updated_at = date_time, - } - Ok(()) - } } -#[derive(Deref, Into, Clone, Copy)] -pub struct Id(pub(crate) u64); +// #[derive(Deref, Into, Clone, Copy)] +// pub struct Id(pub(crate) u64); -#[derive(Validate, Deref, From, Clone)] +#[derive(Validate, Deref, From, Into)] #[garde(transparent)] -pub struct Name(#[garde(alphanumeric, length(min = 2, max = 127))] pub String); +pub struct Name(#[garde(length(chars, max = 127))] pub String); -#[derive(Validate, Deref, From, Clone)] +#[derive(Validate, Deref, From, Into)] #[garde(transparent)] -pub struct Description(#[garde(length(max = 510))] pub Option); +pub struct Description(#[garde(length(chars, max = 510))] pub Option); -pub struct New { - pub name: Name, - pub description: Description, -} - -#[derive(Clone)] pub enum Field { Name(Valid), Description(Valid), @@ -52,28 +28,33 @@ pub enum Field { UpdatedAt(DateTime), } +pub struct New { + pub name: Valid, + pub description: Valid, +} + pub struct Base { - pub(crate) id: Id, - pub(crate) name: Name, - pub(crate) description: Description, + pub(crate) id: u64, + pub(crate) name: String, + pub(crate) description: Option, pub(crate) created_at: DateTime, pub(crate) updated_at: DateTime, } impl Base { - pub fn id(&self) -> Id { + pub const fn id(&self) -> u64 { self.id } - pub fn name(&self) -> &Name { + pub const fn name(&self) -> &String { &self.name } - pub fn description(&self) -> &Description { - &self.description + pub const fn description(&self) -> Option<&String> { + self.description.as_ref() } - pub fn created_at(&self) -> DateTime { + pub const fn created_at(&self) -> DateTime { self.created_at } - pub fn updated_at(&self) -> DateTime { + pub const fn updated_at(&self) -> DateTime { self.updated_at } } diff --git a/src/database/src/port/package.rs b/src/database/src/port/package.rs index 72de830..cdb50b7 100644 --- a/src/database/src/port/package.rs +++ b/src/database/src/port/package.rs @@ -1,168 +1,94 @@ -use super::{Result, package_base::Base}; +pub use super::{CRUD, Result, base::Base}; +pub use chrono::{DateTime, Utc}; use derive_more::{Deref, From, Into}; use garde::{Valid, Validate}; -use sqlx::{Executor, MySql}; - -// pub enum GetBy #[allow(async_fn_in_trait)] -pub trait PackageRepository { - async fn get_by_id(connection: &C, id: Id) -> Result>; - async fn get_by_name(connection: &C, name: &Valid) -> Result>; - - async fn change_name(connection: &mut C, package: &mut Package, name: Valid) -> Result; - async fn change_base(connection: &mut C, package: &mut Package, base: &Base) -> Result; - async fn change_version(connection: &mut C, package: &mut Package, version: Valid) -> Result; - - async fn create(connection: &mut C, data: Valid) -> Result; +pub trait PackageRepository: + CRUD +{ } -#[derive(Deref, Into, Clone, Copy)] -pub struct Id(u32); - -pub type BaseId = super::package_base::Id; - -#[derive(Validate, Deref)] +#[derive(Validate, Deref, From, Into)] #[garde(transparent)] -pub struct Name(#[garde(alphanumeric, length(min = 2, max = 127))] pub String); +pub struct Name(#[garde(length(chars, max = 127))] pub String); -#[derive(Validate, Deref)] +#[derive(Validate, Deref, From, Into)] #[garde(transparent)] -pub struct Version(#[garde(alphanumeric, length(min = 1, max = 127))] pub String); +pub struct Version(#[garde(length(chars, max = 127))] pub String); -#[derive(Validate, Deref)] +#[derive(Validate, Deref, From, Into)] #[garde(transparent)] -pub struct Description(#[garde(ascii, length(max = 255))] pub Option); +pub struct Description(#[garde(length(chars, max = 255))] pub Option); -#[derive(Validate)] +#[derive(Validate, Deref, From, Into)] #[garde(transparent)] -pub struct URL(#[garde(url, length(max = 510))] pub Option); +pub struct URL(#[garde(length(chars, max = 510))] pub Option); -#[derive(Validate)] -pub struct PackageData { - #[garde(dive)] - pub name: Name, - #[garde(dive)] - pub description: Description, +pub enum Unique { + Id(u64), + Name(Valid), +} + +pub enum Field { + PackageBase(Base), + Name(Valid), + Version(Valid), + Description(Valid), + URL(Valid), + FlaggedAt(Option>), + CreatedAt(DateTime), + UpdatedAt(DateTime), +} + +pub struct New { + pub package_base: Base, + pub name: Valid, + pub version: Valid, + pub description: Valid, + pub url: Valid, + pub flagged_at: Option>, } -#[derive(Deref)] pub struct Package { - id: Id, - #[deref] - data: PackageData, + pub(crate) id: u64, + pub(crate) package_base: u64, + pub(crate) name: String, + pub(crate) version: String, + pub(crate) description: Option, + pub(crate) url: Option, + pub(crate) flagged_at: Option>, + pub(crate) created_at: DateTime, + pub(crate) updated_at: DateTime, } + impl Package { - pub const fn id(&self) -> Id { + pub const fn id(&self) -> u64 { self.id } + pub const fn package_base(&self) -> u64 { + self.package_base + } + pub const fn name(&self) -> &String { + &self.name + } + pub const fn version(&self) -> &String { + &self.version + } + pub const fn description(&self) -> Option<&String> { + self.description.as_ref() + } + pub const fn url(&self) -> Option<&String> { + self.url.as_ref() + } + pub const fn flagged_at(&self) -> Option> { + self.flagged_at + } + pub const fn created_at(&self) -> DateTime { + self.created_at + } + pub const fn updated_at(&self) -> DateTime { + self.updated_at + } } - -// pub struct UserAdapter; -// -// struct QueryUser { -// id: u32, -// name: String, -// email: String, -// password: String, -// } -// impl From for Package { -// fn from(value: QueryUser) -> Self { -// Self { -// id: Id(value.id), -// data: PackageData { -// name: Name(value.name), -// description: Description(value.email), -// }, -// } -// } -// } -// -// impl PackageRepository for UserAdapter -// where -// for<'a> &'a E: Executor<'a, Database = MySql>, -// { -// async fn get_by_id(connection: &E, id: Id) -> Result> { -// Ok(sqlx::query_as!( -// QueryUser, -// "SELECT id, name, email, password FROM Users WHERE id = ?", -// id.0 -// ) -// .fetch_optional(connection) -// .await? -// .map(Into::into)) -// } -// async fn get_by_name(connection: &E, name: &Valid) -> Result> { -// Ok(sqlx::query_as!( -// QueryUser, -// "SELECT id, name, email, password FROM Users WHERE name = ?", -// name.0 -// ) -// .fetch_optional(connection) -// .await? -// .map(Into::into)) -// } -// async fn get_by_email(connection: &E, email: &Valid) -> Result> { -// Ok(sqlx::query_as!( -// QueryUser, -// "SELECT id, name, email, password FROM Users WHERE email = ?", -// email.0 -// ) -// .fetch_optional(connection) -// .await? -// .map(Into::into)) -// } -// -// async fn change_name(connection: &mut E, user: &mut Package, name: Valid) -> Result { -// sqlx::query!("UPDATE Users SET name = ? WHERE id = ?", name.0, user.id.0) -// .execute(&*connection) -// .await?; -// Ok(()) -// } -// async fn change_email( -// connection: &mut E, -// user: &mut Package, -// email: Valid, -// ) -> Result { -// sqlx::query!( -// "UPDATE Users SET email = ? WHERE id = ?", -// email.0, -// user.id.0 -// ) -// .execute(&*connection) -// .await?; -// Ok(()) -// } -// async fn change_password( -// connection: &mut E, -// user: &mut Package, -// password: Valid, -// ) -> Result { -// sqlx::query!( -// "UPDATE Users SET password = ? WHERE id = ?", -// password.0, -// user.id.0 -// ) -// .execute(&*connection) -// .await?; -// Ok(()) -// } -// -// async fn create(connection: &mut E, data: Valid) -> Result { -// let id = sqlx::query!( -// "INSERT INTO Users (name, email, password) VALUES (?, ?, ?)", -// data.name.0, -// data.description.0, -// data.password.0 -// ) -// .execute(&*connection) -// .await? -// .last_insert_id() as u32; -// -// Ok(Package { -// id: Id(id), -// data: data.into_inner(), -// }) -// } -// } diff --git a/src/database/src/port/user.rs b/src/database/src/port/user.rs index afa448b..b0a3933 100644 --- a/src/database/src/port/user.rs +++ b/src/database/src/port/user.rs @@ -6,61 +6,28 @@ use garde::{Valid, Validate}; #[allow(async_fn_in_trait)] pub trait UserRepository: - super::CRUD< - C, - Create = New, - Read = Unique, - Update = Field, - Delete = Unique, - Existing = User, - Id = Id, - > + CRUD { - async fn update_user(connection: &mut C, user: &mut User, data: Self::Update) -> Result { - Self::update(connection, user.id, data.clone()).await?; - match data { - Field::Name(valid) => user.name = valid.into_inner(), - Field::Email(valid) => user.email = valid.into_inner(), - Field::Password(valid) => user.password = valid.into_inner(), - Field::LastUsed(date_time) => user.last_used = date_time, - Field::CreatedAt(date_time) => user.created_at = date_time, - Field::UpdatedAt(date_time) => user.updated_at = date_time, - } - Ok(()) - } } -#[derive(Deref, Into, Clone, Copy)] -pub struct Id(pub(crate) u64); - -// TODO: is this the right layer for requirements (email) validatoin? - -#[derive(Validate, Deref, From, Clone)] +#[derive(Validate, Deref, From, Into)] #[garde(transparent)] -pub struct Name(#[garde(alphanumeric, length(min = 2, max = 31))] pub String); +pub struct Name(#[garde(length(chars, max = 31))] pub String); -#[derive(Validate, Deref, From, Clone)] +#[derive(Validate, Deref, From, Into)] #[garde(transparent)] -pub struct Email(#[garde(email, length(max = 255))] pub String); +pub struct Email(#[garde(length(chars, max = 255))] pub String); -#[derive(Validate, Deref, From, Clone)] +#[derive(Validate, Deref, From, Into)] #[garde(transparent)] -pub struct Password(#[garde(ascii, length(max = 255))] pub String); - -pub struct New { - pub name: Valid, - pub email: Valid, - pub password: Valid, - pub last_used: Option>, -} +pub struct Password(#[garde(length(chars, max = 255))] pub String); pub enum Unique { - Id(Id), + Id(u64), Name(Valid), Email(Valid), } -#[derive(Clone)] pub enum Field { Name(Valid), Email(Valid), @@ -70,27 +37,34 @@ pub enum Field { UpdatedAt(DateTime), } +pub struct New { + pub name: Valid, + pub email: Valid, + pub password: Valid, + pub last_used: Option>, +} + pub struct User { - pub(crate) id: Id, - pub(crate) name: Name, - pub(crate) email: Email, - pub(crate) password: Password, + pub(crate) id: u64, + pub(crate) name: String, + pub(crate) email: String, + pub(crate) password: String, pub(crate) last_used: Option>, pub(crate) created_at: DateTime, pub(crate) updated_at: DateTime, } impl User { - pub const fn id(&self) -> Id { + pub const fn id(&self) -> u64 { self.id } - pub const fn name(&self) -> &Name { + pub const fn name(&self) -> &String { &self.name } - pub const fn email(&self) -> &Email { + pub const fn email(&self) -> &String { &self.email } - pub const fn password(&self) -> &Password { + pub const fn password(&self) -> &String { &self.password } pub const fn last_used(&self) -> Option> {