#![feature(drain_filter)] pub mod api; use std::fmt::Formatter; use std::str::FromStr; use derive_more::{Deref, Display, From}; #[cfg(feature = "dummy")] use fake::Fake; use serde::de::{Error, Visitor}; use serde::{Deserialize, Deserializer, Serialize}; #[derive(Debug, thiserror::Error)] pub enum TransformError { #[error("Given value is below minimal value")] BelowMinimal, #[error("Given value is not valid day flag")] NotDay, #[error("Given value is not valid email address")] NotEmail, } pub type RecordId = i32; #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] pub enum OrderStatus { #[display(fmt = "Potwierdzone")] Confirmed, #[display(fmt = "Odebrane")] Delivered, #[display(fmt = "Opłacone")] Payed, #[display(fmt = "Anulowane")] Cancelled, #[display(fmt = "Wymaga zwrotu płatności")] RequireRefund, #[display(fmt = "Płatność zwrócona")] Refunded, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "snake_case")] pub enum Role { #[display(fmt = "Adminitrator")] Admin, #[display(fmt = "Użytkownik")] User, } impl PartialEq<&str> for Role { fn eq(&self, other: &&str) -> bool { self.as_str() == *other } } impl Role { pub fn as_str(&self) -> &str { match self { Role::Admin => "Admin", Role::User => "User", } } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] pub enum QuantityUnit { #[cfg_attr(feature = "db", sqlx(rename = "g"))] Gram, #[cfg_attr(feature = "db", sqlx(rename = "dkg"))] Decagram, #[cfg_attr(feature = "db", sqlx(rename = "kg"))] Kilogram, #[cfg_attr(feature = "db", sqlx(rename = "piece"))] Piece, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] pub enum PaymentMethod { PayU, PaymentOnTheSpot, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] pub enum ShoppingCartState { Active, Closed, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "snake_case")] pub enum Audience { Web, Mobile, Feed, AdminPanel, } impl PartialEq<&str> for Audience { fn eq(&self, other: &&str) -> bool { self.as_str() == *other } } impl Audience { pub fn as_str(&self) -> &str { match self { Audience::Web => "Web", Audience::Mobile => "Mobile", Audience::Feed => "Feed", Audience::AdminPanel => "AdminPanel", } } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(rename_all = "snake_case"))] #[derive(Copy, Clone, Debug, Display, Deserialize, Serialize, PartialEq)] #[serde(rename_all = "snake_case")] pub enum AccountState { Active, Suspended, Banned, } impl Default for Audience { fn default() -> Self { Self::Web } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Default, Debug, Copy, Clone, Deref, From)] #[serde(transparent)] pub struct Price(NonNegative); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Default, Debug, Copy, Clone, Deref, From)] #[serde(transparent)] pub struct Quantity(NonNegative); impl TryFrom for Quantity { type Error = TransformError; fn try_from(value: i32) -> Result { Ok(Self(value.try_into()?)) } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "dummy", dummy("fake::faker::internet::en::Username"))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Deserialize, Serialize, Debug, Deref, From, Display)] #[serde(transparent)] pub struct Login(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "dummy", dummy("fake::faker::internet::en::FreeEmail"))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Debug, Deref, From, Display)] #[serde(transparent)] pub struct Email(String); impl FromStr for Email { type Err = TransformError; fn from_str(s: &str) -> Result { if validator::validate_email(s) { Ok(Self(String::from(s))) } else { Err(TransformError::NotEmail) } } } impl<'de> serde::Deserialize<'de> for Email { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct EmailVisitor {} impl<'v> Visitor<'v> for EmailVisitor { type Value = String; fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { formatter.write_str("valid e-mail address") } fn visit_str(self, s: &str) -> Result where E: Error, { if validator::validate_email(s) { Ok(String::from(s)) } else { Err(E::custom("this is not email address")) } } } Ok(Email(deserializer.deserialize_str(EmailVisitor {})?)) } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Default, Debug, Copy, Clone, Deref, Display)] #[serde(transparent)] pub struct NonNegative(i32); impl TryFrom for NonNegative { type Error = TransformError; fn try_from(value: i32) -> Result { if value < 0 { Err(TransformError::BelowMinimal) } else { Ok(Self(value)) } } } impl<'de> serde::Deserialize<'de> for NonNegative { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct NonNegativeVisitor; impl<'v> Visitor<'v> for NonNegativeVisitor { type Value = i32; fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { formatter.write_str("value equal or greater than 0") } fn visit_i32(self, v: i32) -> Result where E: Error, { if v >= 0 { Ok(v) } else { Err(E::custom("Value must be equal or greater than 0")) } } fn visit_i64(self, v: i64) -> Result where E: Error, { let v = v .try_into() .map_err(|_| E::custom("Value must be equal or greater than 0"))?; if v >= 0 { Ok(v) } else { Err(E::custom("Value must be equal or greater than 0")) } } fn visit_u32(self, v: u32) -> Result where E: Error, { let v = v .try_into() .map_err(|_| E::custom("Value must be equal or greater than 0"))?; Ok(v) } fn visit_u64(self, v: u64) -> Result where E: Error, { let v = v .try_into() .map_err(|_| E::custom("Value must be equal or greater than 0"))?; Ok(v) } } Ok(NonNegative( deserializer.deserialize_i32(NonNegativeVisitor)?, )) } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[derive(Serialize, Deserialize, Debug, Copy, Clone, Display, From)] #[serde(rename_all = "lowercase")] pub enum Day { Monday = 1 << 0, Tuesday = 1 << 1, Wednesday = 1 << 2, Thursday = 1 << 3, Friday = 1 << 4, Saturday = 1 << 5, Sunday = 1 << 6, } impl TryFrom for Day { type Error = TransformError; fn try_from(value: i32) -> Result { if value == (Day::Monday as i32) { Ok(Day::Monday) } else if value == (Day::Tuesday as i32) { Ok(Day::Tuesday) } else if value == (Day::Wednesday as i32) { Ok(Day::Wednesday) } else if value == (Day::Thursday as i32) { Ok(Day::Thursday) } else if value == (Day::Friday as i32) { Ok(Day::Friday) } else if value == (Day::Saturday as i32) { Ok(Day::Saturday) } else if value == (Day::Sunday as i32) { Ok(Day::Sunday) } else { Err(TransformError::NotDay) } } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[derive(Serialize, Deserialize, Deref, Debug)] #[serde(transparent)] pub struct Days(Vec); #[cfg(feature = "db")] impl<'q> ::sqlx::encode::Encode<'q, sqlx::Postgres> for Days where i32: ::sqlx::encode::Encode<'q, sqlx::Postgres>, { fn encode_by_ref( &self, buf: &mut >::ArgumentBuffer, ) -> ::sqlx::encode::IsNull { let value = self.0.iter().fold(1, |memo, v| memo | *v as i32); >::encode_by_ref(&value, buf) } fn size_hint(&self) -> usize { >::size_hint(&Default::default()) } } #[cfg(feature = "db")] impl<'r> ::sqlx::decode::Decode<'r, sqlx::Postgres> for Days where i32: ::sqlx::decode::Decode<'r, sqlx::Postgres>, { fn decode( value: >::ValueRef, ) -> ::std::result::Result< Self, ::std::boxed::Box< dyn ::std::error::Error + 'static + ::std::marker::Send + ::std::marker::Sync, >, > { let value = >::decode(value)?; Ok(Days( (0..9) .into_iter() .filter_map(|n| { eprintln!( "d {} {} {} {:?}", n, 1 << n, value & 1 << n, Day::try_from(value & 1 << n).ok() ); Day::try_from(value & 1 << n).ok() }) .collect(), )) } } #[cfg(feature = "db")] impl sqlx::Type for Days where i32: ::sqlx::Type, { fn type_info() -> ::TypeInfo { >::type_info() } fn compatible(ty: &::TypeInfo) -> bool { >::compatible(ty) } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, From, Display)] #[serde(transparent)] pub struct Password(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, From, Display)] #[serde(transparent)] pub struct PasswordConfirmation(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, From, Display)] #[serde(transparent)] pub struct PassHash(String); impl PartialEq for Password { fn eq(&self, other: &PasswordConfirmation) -> bool { self.0 == other.0 } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, Deref, Display, From)] #[serde(transparent)] pub struct AccountId(RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct FullAccount { pub id: AccountId, pub email: Email, pub login: Login, pub pass_hash: PassHash, pub role: Role, pub customer_id: uuid::Uuid, pub state: AccountState, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct Account { pub id: AccountId, pub email: Email, pub login: Login, pub role: Role, pub customer_id: uuid::Uuid, pub state: AccountState, } impl From for Account { fn from( FullAccount { id, email, login, pass_hash: _, role, customer_id, state, }: FullAccount, ) -> Self { Self { id, email, login, role, customer_id, state, } } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Hash, Deref, Display, From)] #[serde(transparent)] pub struct ProductId(RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Clone, Deref, Display, From)] #[serde(transparent)] pub struct ProductName(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Clone, Deref, Display, From)] #[serde(transparent)] pub struct ProductShortDesc(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Clone, Deref, Display, From)] #[serde(transparent)] pub struct ProductLongDesc(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Clone, Deref, Display, From)] #[serde(transparent)] pub struct ProductCategory(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct Product { pub id: ProductId, pub name: ProductName, pub short_description: ProductShortDesc, pub long_description: ProductLongDesc, pub category: Option, pub price: Price, pub deliver_days_flag: Days, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize)] #[serde(transparent)] pub struct StockId(pub RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct Stock { pub id: StockId, pub product_id: ProductId, pub quantity: Quantity, pub quantity_unit: QuantityUnit, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq, Display, Deref)] #[serde(transparent)] pub struct AccountOrderId(RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Display, Deref)] #[serde(transparent)] pub struct OrderId(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct AccountOrder { pub id: AccountOrderId, pub buyer_id: AccountId, pub status: OrderStatus, pub order_id: Option, pub order_ext_id: uuid::Uuid, pub service_order_id: Option, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct PublicAccountOrder { pub id: AccountOrderId, pub buyer_id: AccountId, pub status: OrderStatus, pub order_id: Option, } impl From for PublicAccountOrder { fn from( AccountOrder { id, buyer_id, status, order_id, order_ext_id: _, service_order_id: _, }: AccountOrder, ) -> Self { Self { id, buyer_id, status, order_id, } } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, Deref)] pub struct OrderItemId(pub RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize, Debug)] pub struct OrderItem { pub id: OrderItemId, pub product_id: ProductId, pub order_id: AccountOrderId, pub quantity: Quantity, pub quantity_unit: QuantityUnit, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, Deref, Display)] #[serde(transparent)] pub struct ShoppingCartId(pub RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct ShoppingCart { pub id: ShoppingCartId, pub buyer_id: AccountId, pub payment_method: PaymentMethod, pub state: ShoppingCartState, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Debug, Deref, Display)] #[serde(transparent)] pub struct ShoppingCartItemId(RecordId); #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct ShoppingCartItem { pub id: ShoppingCartId, pub product_id: ProductId, pub shopping_cart_id: ShoppingCartId, pub quantity: Quantity, pub quantity_unit: QuantityUnit, } impl ShoppingCartItem { pub fn quantity(&self) -> Quantity { self.quantity } } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Copy, Clone, Deref, Display, Debug)] #[serde(transparent)] pub struct TokenId(RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize)] pub struct Token { pub id: TokenId, pub customer_id: uuid::Uuid, pub role: Role, /// iss (issuer): Issuer of the JWT pub issuer: String, /// sub (subject): Subject of the JWT (the user) pub subject: i32, /// aud (audience): Recipient for which the JWT is intended pub audience: Audience, /// exp (expiration time): Time after which the JWT expires pub expiration_time: chrono::NaiveDateTime, /// nbt (not before time): Time before which the JWT must not be accepted /// for processing pub not_before_time: chrono::NaiveDateTime, /// iat (issued at time): Time at which the JWT was issued; can be used to /// determine age of the JWT, pub issued_at_time: chrono::NaiveDateTime, /// jti (JWT ID): Unique identifier; can be used to prevent the JWT from /// being replayed (allows a token to be used only once) pub jwt_id: uuid::Uuid, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, Display, From)] pub struct TokenString(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, Display, From)] pub struct LocalPath(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, Display, From)] pub struct FileName(String); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, Display, From)] pub struct PhotoId(RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::Type))] #[cfg_attr(feature = "db", sqlx(transparent))] #[derive(Serialize, Deserialize, Debug, Deref, Display, From)] pub struct ProductPhotoId(RecordId); #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize, Debug)] pub struct Photo { pub id: PhotoId, pub local_path: LocalPath, pub file_name: FileName, } #[cfg_attr(feature = "dummy", derive(fake::Dummy))] #[cfg_attr(feature = "db", derive(sqlx::FromRow))] #[derive(Serialize, Deserialize, Debug)] pub struct ProductPhoto { pub id: ProductPhotoId, pub product_id: ProductId, pub photo_id: PhotoId, }