diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index b17ade72..7945d6dc 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -5,6 +5,7 @@ use async_trait::async_trait; use sea_query::{Nullable, ValueTuple}; use std::fmt::Debug; +/// Defines a value from an ActiveModel and its state #[derive(Clone, Debug, Default)] pub struct ActiveValue where @@ -14,6 +15,7 @@ where state: ActiveValueState, } +/// Defines a set operation on an [ActiveValue] #[allow(non_snake_case)] pub fn Set(v: V) -> ActiveValue where @@ -22,6 +24,7 @@ where ActiveValue::set(v) } +/// Defines an unset operation on an [ActiveValue] #[allow(non_snake_case)] pub fn Unset(_: Option) -> ActiveValue where @@ -30,6 +33,7 @@ where ActiveValue::unset() } +// Defines the state of an [ActiveValue] #[derive(Clone, Debug)] enum ActiveValueState { Set, @@ -51,22 +55,31 @@ where ActiveValue::unchanged(value) } +/// Enforces a set of constraints on any type performing an Create, Update or Delete operation #[async_trait] pub trait ActiveModelTrait: Clone + Debug { + /// Enforce the type to the constraints of the [EntityTrait] type Entity: EntityTrait; + /// Get a mutable [ActiveValue] from an ActiveModel fn take(&mut self, c: ::Column) -> ActiveValue; + /// Get a immutable [ActiveValue] from an ActiveModel fn get(&self, c: ::Column) -> ActiveValue; + /// Set the Value into an ActiveModel fn set(&mut self, c: ::Column, v: Value); + /// Set the state of an [ActiveValue] to the Unset state fn unset(&mut self, c: ::Column); + /// Check the state of a [ActiveValue] fn is_unset(&self, c: ::Column) -> bool; + /// The default implementation of the ActiveModel fn default() -> Self; + /// Get the primary key of the ActiveModel #[allow(clippy::question_mark)] fn get_primary_key_value(&self) -> Option { let mut cols = ::PrimaryKey::iter(); @@ -103,6 +116,7 @@ pub trait ActiveModelTrait: Clone + Debug { } } + /// Perform an `INSERT` operation on the ActiveModel async fn insert<'a, C>(self, db: &'a C) -> Result where ::Model: IntoActiveModel, @@ -121,6 +135,7 @@ pub trait ActiveModelTrait: Clone + Debug { ActiveModelBehavior::after_save(am, true) } + /// Perform the `UPDATE` operation on an ActiveModel async fn update<'a, C>(self, db: &'a C) -> Result where Self: ActiveModelBehavior + 'a, @@ -170,6 +185,7 @@ pub trait ActiveModelTrait: Clone + Debug { } } +/// Enforce a set of constraints to a override the ActiveModel behavior /// Behaviors for users to override #[allow(unused_variables)] pub trait ActiveModelBehavior: ActiveModelTrait { @@ -199,10 +215,12 @@ pub trait ActiveModelBehavior: ActiveModelTrait { } } +/// Enforce constraints for conversion to an ActiveModel pub trait IntoActiveModel where A: ActiveModelTrait, { + /// Method to call to perform the conversion fn into_active_model(self) -> A; } @@ -215,10 +233,12 @@ where } } +/// Constraints to perform the conversion of a type into an [ActiveValue] pub trait IntoActiveValue where V: Into, { + /// Method to perform the conversion fn into_active_value(self) -> ActiveValue; } @@ -296,6 +316,7 @@ impl ActiveValue where V: Into, { + /// Set the value of an [ActiveValue] and also set its state to `ActiveValueState::Set` pub fn set(value: V) -> Self { Self { value: Some(value), @@ -303,6 +324,7 @@ where } } + /// Check if the state of an [ActiveValue] is `ActiveValueState::Set` which returns true pub fn is_set(&self) -> bool { matches!(self.state, ActiveValueState::Set) } @@ -314,10 +336,14 @@ where } } + /// Check if the status of the [ActiveValue] is `ActiveValueState::Unchanged` + /// which returns `true` if it is pub fn is_unchanged(&self) -> bool { matches!(self.state, ActiveValueState::Unchanged) } + /// Set the `value` field of the ActiveModel to [Option::None] and the + /// `state` field to `ActiveValueState::Unset` pub fn unset() -> Self { Self { value: None, @@ -325,23 +351,30 @@ where } } + /// Check if the state of an [ActiveValue] is `ActiveValueState::Unset` + /// which returns true if it is pub fn is_unset(&self) -> bool { matches!(self.state, ActiveValueState::Unset) } + /// Get the mutable value of the `value` field of an [ActiveValue] + /// also setting it's state to `ActiveValueState::Unset` pub fn take(&mut self) -> Option { self.state = ActiveValueState::Unset; self.value.take() } + /// Get an owned value of the `value` field of the [ActiveValue] pub fn unwrap(self) -> V { self.value.unwrap() } + /// Check is a [Value] exists or not pub fn into_value(self) -> Option { self.value.map(Into::into) } + /// Wrap the [Value] into a `ActiveValue` pub fn into_wrapped_value(self) -> ActiveValue { match self.state { ActiveValueState::Set => ActiveValue::set(self.into_value().unwrap()), diff --git a/src/entity/base_entity.rs b/src/entity/base_entity.rs index aef46207..4d3e1d87 100644 --- a/src/entity/base_entity.rs +++ b/src/entity/base_entity.rs @@ -7,21 +7,28 @@ use sea_query::{Alias, Iden, IntoIden, IntoTableRef, IntoValueTuple, TableRef}; pub use sea_strum::IntoEnumIterator as Iterable; use std::fmt::Debug; +/// Ensure the identifier for an Entity can be converted to a static str pub trait IdenStatic: Iden + Copy + Debug + 'static { + /// Method to call to get the static string identity fn as_str(&self) -> &str; } +/// Enforces the naming of an entity to a set of constraints pub trait EntityName: IdenStatic + Default { + /// Method to get the name for the schema, defaults to [Option::None] if not set fn schema_name(&self) -> Option<&str> { None } + /// Get the name of the table fn table_name(&self) -> &str; + /// Get the name of the module from the invoking `self.table_name()` fn module_name(&self) -> &str { self.table_name() } + /// Get the [TableRef] from invoking the `self.schema_name()` fn table_ref(&self) -> TableRef { match self.schema_name() { Some(schema) => (Alias::new(schema).into_iden(), self.into_iden()).into_table_ref(), @@ -43,14 +50,19 @@ pub trait EntityName: IdenStatic + Default { /// - Update: `update`, `update_*` /// - Delete: `delete`, `delete_*` pub trait EntityTrait: EntityName { + #[allow(missing_docs)] type Model: ModelTrait + FromQueryResult; + #[allow(missing_docs)] type Column: ColumnTrait; + #[allow(missing_docs)] type Relation: RelationTrait; + #[allow(missing_docs)] type PrimaryKey: PrimaryKeyTrait + PrimaryKeyToColumn; + /// Check if the relation belongs to an Entity fn belongs_to(related: R) -> RelationBuilder where R: EntityTrait, @@ -58,6 +70,7 @@ pub trait EntityTrait: EntityName { RelationBuilder::new(RelationType::HasOne, Self::default(), related, false) } + /// Check if the entity has at least one relation fn has_one(_: R) -> RelationBuilder where R: EntityTrait + Related, @@ -65,6 +78,7 @@ pub trait EntityTrait: EntityName { RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true) } + /// Chech if the Entity has many relations fn has_many(_: R) -> RelationBuilder where R: EntityTrait + Related, diff --git a/src/entity/column.rs b/src/entity/column.rs index 423e3e3b..bb92d46d 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -2,6 +2,7 @@ use crate::{EntityName, IdenStatic, Iterable}; use sea_query::{DynIden, Expr, SeaRc, SelectStatement, SimpleExpr, Value}; use std::str::FromStr; +/// Defines a Column for an Entity #[derive(Debug, Clone, PartialEq)] pub struct ColumnDef { pub(crate) col_type: ColumnType, @@ -10,34 +11,62 @@ pub struct ColumnDef { pub(crate) indexed: bool, } +/// The type of column as defined in the SQL format #[derive(Debug, Clone, PartialEq)] pub enum ColumnType { + /// `CHAR` type of specified fixed length Char(Option), + /// `STRING` type for variable string length String(Option), + /// `TEXT` type used for large pieces of string data and stored out of row in case size is too big Text, + /// `TINYINT` useful for storing one byte of data (range of 0-255) TinyInteger, + /// `SMALLINT` data type stores small whole numbers that range from –32,767 to 32,767 SmallInteger, + /// `INTEGER` data types hold numbers that are whole, or without a decimal point Integer, + /// `BIGINT` is a 64-bit representation of an integer taking up 8 bytes of storage and + /// ranging from -2^63 (-9,223,372,036,854,775,808) to 2^63 (9,223,372,036,854,775,807). BigInteger, + /// `FLOAT` an approximate-number data type, where values range cannot be represented exactly. Float, + /// `DOUBLE` is a normal-size floating point number where the + /// total number of digits is specified in size. Double, + /// `DECIMAL` type store numbers that have fixed precision and scale Decimal(Option<(u32, u32)>), + /// `DATETIME` type is used for values that contain both date and time parts. DateTime, + /// `TIMESTAMP` is a temporal data type that holds the combination of date and time. Timestamp, + /// `TIMESTAMP WITH TIME ZONE` (or `TIMESTAMPTZ`) data type stores 8-byte + /// date values that include timestamp and time zone information in UTC format. TimestampWithTimeZone, + /// `TIME` data type defines a time of a day based on 24-hour clock Time, + /// `DATE` data type stores the calendar date Date, + /// `BINARY` data types contain byte strings—a sequence of octets or bytes. Binary, + /// `BOOLEAN` is the result of a comparison operator Boolean, + /// `MONEY` data type handles monetary data Money(Option<(u32, u32)>), + /// `JSON` represents the JavaScript Object Notation type Json, + /// JSON binary format is structured in the way that permits the server to search for + /// values within the JSON document directly by key or array index, which is very fast. JsonBinary, + /// A custom implementation of a data type Custom(String), + /// A Universally Unique IDentifier that is specified in RFC 4122 Uuid, } macro_rules! bind_oper { ( $op: ident ) => { + #[allow(missing_docs)] fn $op(&self, v: V) -> SimpleExpr where V: Into, @@ -58,6 +87,7 @@ macro_rules! bind_func_no_params { macro_rules! bind_vec_func { ( $func: ident ) => { + #[allow(missing_docs)] #[allow(clippy::wrong_self_convention)] fn $func(&self, v: I) -> SimpleExpr where @@ -72,6 +102,7 @@ macro_rules! bind_vec_func { macro_rules! bind_subquery_func { ( $func: ident ) => { #[allow(clippy::wrong_self_convention)] + #[allow(missing_docs)] fn $func(&self, s: SelectStatement) -> SimpleExpr { Expr::tbl(self.entity_name(), *self).$func(s) } @@ -81,14 +112,18 @@ macro_rules! bind_subquery_func { // LINT: when the operand value does not match column type /// Wrapper of the identically named method in [`sea_query::Expr`] pub trait ColumnTrait: IdenStatic + Iterable + FromStr { + #[allow(missing_docs)] type EntityName: EntityName; + /// Define a column for an Entity fn def(&self) -> ColumnDef; + /// Get the name of the entity the column belongs to fn entity_name(&self) -> DynIden { SeaRc::new(Self::EntityName::default()) as DynIden } + /// get the name of the entity the column belongs to fn as_column_ref(&self) -> (DynIden, DynIden) { (self.entity_name(), SeaRc::new(*self) as DynIden) } @@ -221,6 +256,7 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr { bind_func_no_params!(is_null); bind_func_no_params!(is_not_null); + /// Perform an operation if the column is null fn if_null(&self, v: V) -> SimpleExpr where V: Into, @@ -236,6 +272,7 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr { } impl ColumnType { + /// instantiate a new [ColumnDef] pub fn def(self) -> ColumnDef { ColumnDef { col_type: self, @@ -247,20 +284,24 @@ impl ColumnType { } impl ColumnDef { + /// Marks the column as `UNIQUE` pub fn unique(mut self) -> Self { self.unique = true; self } + /// Mark the column as nullable pub fn null(self) -> Self { self.nullable() } + /// Mark the column as nullable pub fn nullable(mut self) -> Self { self.null = true; self } + /// Set the `indexed` field to `true` pub fn indexed(mut self) -> Self { self.indexed = true; self diff --git a/src/entity/identity.rs b/src/entity/identity.rs index d8623b7d..b9316f9f 100644 --- a/src/entity/identity.rs +++ b/src/entity/identity.rs @@ -2,10 +2,14 @@ use crate::{ColumnTrait, EntityTrait, IdenStatic}; use sea_query::{Alias, DynIden, Iden, IntoIden, SeaRc}; use std::fmt; +/// Defines an operation for an Entity #[derive(Debug, Clone)] pub enum Identity { + /// Performs one operation Unary(DynIden), + /// Performs two operations Binary(DynIden, DynIden), + /// Performs three operations Ternary(DynIden, DynIden, DynIden), } @@ -28,14 +32,18 @@ impl Iden for Identity { } } +/// Performs a conversion into an [Identity] pub trait IntoIdentity { + /// Method to perform the conversion fn into_identity(self) -> Identity; } +/// Check the [Identity] of an Entity pub trait IdentityOf where E: EntityTrait, { + /// Method to call to perform this check fn identity_of(self) -> Identity; } diff --git a/src/entity/link.rs b/src/entity/link.rs index d0c262d7..53d23b97 100644 --- a/src/entity/link.rs +++ b/src/entity/link.rs @@ -3,15 +3,21 @@ use crate::{ }; use sea_query::{Alias, IntoIden, JoinType, SeaRc}; +/// Same as [RelationDef] pub type LinkDef = RelationDef; +/// A set of constraints for links between Entities pub trait Linked { + #[allow(missing_docs)] type FromEntity: EntityTrait; + #[allow(missing_docs)] type ToEntity: EntityTrait; + /// Link for an Entity fn link(&self) -> Vec; + /// Find all the Entities that are linked to the Entity fn find_linked(&self) -> Select { let mut select = Select::new(); for (i, rel) in self.link().into_iter().rev().enumerate() { diff --git a/src/entity/mod.rs b/src/entity/mod.rs index c6d15052..f78795dc 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -4,6 +4,7 @@ mod column; mod identity; mod link; mod model; +/// Re-export common types from the entity pub mod prelude; mod primary_key; mod relation; diff --git a/src/entity/model.rs b/src/entity/model.rs index cd96b22e..3f162dbe 100644 --- a/src/entity/model.rs +++ b/src/entity/model.rs @@ -5,13 +5,18 @@ use crate::{ pub use sea_query::Value; use std::fmt::Debug; +/// A set of constraints for a Model pub trait ModelTrait: Clone + Send + Debug { + #[allow(missing_docs)] type Entity: EntityTrait; + /// Get the [Value] of a column from an Entity fn get(&self, c: ::Column) -> Value; + /// Set the [Value] of a column in an Entity fn set(&mut self, c: ::Column, v: Value); + /// Find related Models fn find_related(&self, _: R) -> Select where R: EntityTrait, @@ -20,6 +25,7 @@ pub trait ModelTrait: Clone + Send + Debug { >::find_related().belongs_to(self) } + /// Find linked Models fn find_linked(&self, l: L) -> Select where L: Linked, @@ -29,9 +35,13 @@ pub trait ModelTrait: Clone + Send + Debug { } } +/// A set of constraints for implementing a [QueryResult] pub trait FromQueryResult: Sized { + /// Instantiate a Model from a [QueryResult] fn from_query_result(res: &QueryResult, pre: &str) -> Result; + /// Transform the error from instantiating a Model from a [QueryResult] + /// and converting it to an [Option] fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result, DbErr> { Ok(Self::from_query_result(res, pre).ok()) } diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index ac4d50fa..67284b68 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -23,6 +23,7 @@ pub use chrono::NaiveTime as Time; #[cfg(feature = "with-chrono")] pub use chrono::NaiveDateTime as DateTime; +/// Handles the time and dates #[cfg(feature = "with-chrono")] pub type DateTimeWithTimeZone = chrono::DateTime; diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index a5e4cde0..17015fa4 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -4,7 +4,9 @@ use sea_query::{FromValueTuple, IntoValueTuple}; use std::fmt::Debug; //LINT: composite primary key cannot auto increment +/// A set of constraints to be used to define a Primary Key pub trait PrimaryKeyTrait: IdenStatic + Iterable { + #[allow(missing_docs)] type ValueType: Sized + Send + Debug @@ -14,14 +16,19 @@ pub trait PrimaryKeyTrait: IdenStatic + Iterable { + TryGetableMany + TryFromU64; + /// Method to call to perform `AUTOINCREMENT` operation on a Primary Kay fn auto_increment() -> bool; } +/// How to map a Primary Key to a column pub trait PrimaryKeyToColumn { + #[allow(missing_docs)] type Column: ColumnTrait; + /// Method to map a primary key to a column in an Entity fn into_column(self) -> Self::Column; + /// Method to map a primary key from a column in an Entity fn from_column(col: Self::Column) -> Option where Self: Sized; diff --git a/src/entity/relation.rs b/src/entity/relation.rs index 93c00596..0733c982 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -3,45 +3,68 @@ use core::marker::PhantomData; use sea_query::{JoinType, TableRef}; use std::fmt::Debug; +/// Defines the type of relationship #[derive(Clone, Debug)] pub enum RelationType { + /// An Entity has one relationship HasOne, + /// An Entity has many relationships HasMany, } +/// Action to perform on a foreign key whenever there are changes +/// to an ActiveModel pub type ForeignKeyAction = sea_query::ForeignKeyAction; +/// Constraints a type to implement the trait to create a relationship pub trait RelationTrait: Iterable + Debug + 'static { + /// The method to call fn def(&self) -> RelationDef; } +/// Checks if Entities are related pub trait Related where R: EntityTrait, { + /// Check if an entity is related to another entity fn to() -> RelationDef; + /// Check if an entity is related through another entity fn via() -> Option { None } + /// Find related Entities fn find_related() -> Select { Select::::new().join_join_rev(JoinType::InnerJoin, Self::to(), Self::via()) } } +/// Defines a relationship #[derive(Debug)] pub struct RelationDef { + /// The type of relationship defined in [RelationType] pub rel_type: RelationType, + /// Reference from another Entity pub from_tbl: TableRef, + /// Reference to another ENtity pub to_tbl: TableRef, + /// Reference to from a Column pub from_col: Identity, + /// Reference to another column pub to_col: Identity, + /// Defines the owner of the Relation pub is_owner: bool, + /// Defines an operation to be performed on a Foreign Key when a + /// `DELETE` Operation is performed pub on_delete: Option, + /// Defines an operation to be performed on a Foreign Key when a + /// `UPDATE` Operation is performed pub on_update: Option, } +/// Defines a helper to build a relation #[derive(Debug)] pub struct RelationBuilder where @@ -108,6 +131,7 @@ where } } + /// Build a relationship from an Entity pub fn from(mut self, identifier: T) -> Self where T: IdentityOf, @@ -116,6 +140,7 @@ where self } + /// Build a relationship to an Entity pub fn to(mut self, identifier: T) -> Self where T: IdentityOf, @@ -124,11 +149,13 @@ where self } + /// An operation to perform on a foreign key when a delete operation occurs pub fn on_delete(mut self, action: ForeignKeyAction) -> Self { self.on_delete = Some(action); self } + /// An operation to perform on a foreign key when an update operation occurs pub fn on_update(mut self, action: ForeignKeyAction) -> Self { self.on_update = Some(action); self