From 497bdbe3dda8f9f060eba55104ae427a3abe7435 Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Thu, 28 Oct 2021 15:46:38 +0300 Subject: [PATCH 01/18] Document the macros. Add code examples for the macros --- sea-orm-macros/src/attributes.rs | 2 + sea-orm-macros/src/derives/active_model.rs | 1 + .../src/derives/active_model_behavior.rs | 1 + sea-orm-macros/src/derives/column.rs | 4 + sea-orm-macros/src/derives/entity_model.rs | 1 + .../src/derives/from_query_result.rs | 1 + .../src/derives/into_active_model.rs | 2 + sea-orm-macros/src/derives/model.rs | 1 + sea-orm-macros/src/derives/primary_key.rs | 1 + sea-orm-macros/src/derives/relation.rs | 1 + sea-orm-macros/src/lib.rs | 123 ++++++++++++++++++ 11 files changed, 138 insertions(+) diff --git a/sea-orm-macros/src/attributes.rs b/sea-orm-macros/src/attributes.rs index fbfa0778..3f479bb9 100644 --- a/sea-orm-macros/src/attributes.rs +++ b/sea-orm-macros/src/attributes.rs @@ -1,6 +1,7 @@ pub mod derive_attr { use bae::FromAttributes; + /// Attributes for Models and ActiveModels #[derive(Default, FromAttributes)] pub struct SeaOrm { pub column: Option, @@ -16,6 +17,7 @@ pub mod derive_attr { pub mod field_attr { use bae::FromAttributes; + /// Operations for Models and ActiveModels #[derive(Default, FromAttributes)] pub struct SeaOrm { pub belongs_to: Option, diff --git a/sea-orm-macros/src/derives/active_model.rs b/sea-orm-macros/src/derives/active_model.rs index add31f20..d691a724 100644 --- a/sea-orm-macros/src/derives/active_model.rs +++ b/sea-orm-macros/src/derives/active_model.rs @@ -4,6 +4,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::{punctuated::Punctuated, token::Comma, Data, DataStruct, Field, Fields, Lit, Meta, Type}; +/// Method to derive an [ActiveModel](sea_orm::ActiveModel) pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result { let fields = match data { Data::Struct(DataStruct { diff --git a/sea-orm-macros/src/derives/active_model_behavior.rs b/sea-orm-macros/src/derives/active_model_behavior.rs index 7ae9d375..0ff02f34 100644 --- a/sea-orm-macros/src/derives/active_model_behavior.rs +++ b/sea-orm-macros/src/derives/active_model_behavior.rs @@ -2,6 +2,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::Data; +/// Method to derive an implementation of [ActiveModelBehavior](sea_orm::ActiveModelBehavior) pub fn expand_derive_active_model_behavior(_ident: Ident, _data: Data) -> syn::Result { Ok(quote!( #[automatically_derived] diff --git a/sea-orm-macros/src/derives/column.rs b/sea-orm-macros/src/derives/column.rs index 7446340e..ed035e0b 100644 --- a/sea-orm-macros/src/derives/column.rs +++ b/sea-orm-macros/src/derives/column.rs @@ -3,6 +3,8 @@ use proc_macro2::{Ident, TokenStream}; use quote::{quote, quote_spanned}; use syn::{punctuated::Punctuated, token::Comma, Data, DataEnum, Fields, Lit, Meta, Variant}; +/// FIXME +/// Method to derive default column names from a &str pub fn impl_default_as_str(ident: &Ident, data: &Data) -> syn::Result { let variants = match data { syn::Data::Enum(DataEnum { variants, .. }) => variants, @@ -65,6 +67,7 @@ pub fn impl_default_as_str(ident: &Ident, data: &Data) -> syn::Result syn::Result { let data_enum = match data { Data::Enum(data_enum) => data_enum, @@ -114,6 +117,7 @@ pub fn expand_derive_column(ident: &Ident, data: &Data) -> syn::Result syn::Result { let impl_default_as_str = impl_default_as_str(ident, data)?; let impl_col_from_str = impl_col_from_str(ident, data)?; diff --git a/sea-orm-macros/src/derives/entity_model.rs b/sea-orm-macros/src/derives/entity_model.rs index 3329731a..9b649922 100644 --- a/sea-orm-macros/src/derives/entity_model.rs +++ b/sea-orm-macros/src/derives/entity_model.rs @@ -7,6 +7,7 @@ use syn::{ Lit, Meta, }; +/// Method to derive an Model pub fn expand_derive_entity_model(data: Data, attrs: Vec) -> syn::Result { // if #[sea_orm(table_name = "foo", schema_name = "bar")] specified, create Entity struct let mut table_name = None; diff --git a/sea-orm-macros/src/derives/from_query_result.rs b/sea-orm-macros/src/derives/from_query_result.rs index fcef9da3..dc6bbc44 100644 --- a/sea-orm-macros/src/derives/from_query_result.rs +++ b/sea-orm-macros/src/derives/from_query_result.rs @@ -2,6 +2,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::{Data, DataStruct, Field, Fields}; +/// Method to derive a [QueryResult](sea_orm::QueryResult) pub fn expand_derive_from_query_result(ident: Ident, data: Data) -> syn::Result { let fields = match data { Data::Struct(DataStruct { diff --git a/sea-orm-macros/src/derives/into_active_model.rs b/sea-orm-macros/src/derives/into_active_model.rs index 3d95e927..2f7dc3f2 100644 --- a/sea-orm-macros/src/derives/into_active_model.rs +++ b/sea-orm-macros/src/derives/into_active_model.rs @@ -2,6 +2,7 @@ use bae::FromAttributes; use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned}; +/// Attributes to derive an ActiveModel #[derive(Default, FromAttributes)] pub struct SeaOrm { pub active_model: Option, @@ -89,6 +90,7 @@ impl IntoActiveModel { } } +/// Method to derive the ActiveModel from the [ActiveModelTrait](sea_orm::ActiveModelTrait) pub fn expand_into_active_model(input: syn::DeriveInput) -> syn::Result { let ident_span = input.ident.span(); diff --git a/sea-orm-macros/src/derives/model.rs b/sea-orm-macros/src/derives/model.rs index 85d690f2..97ca66a8 100644 --- a/sea-orm-macros/src/derives/model.rs +++ b/sea-orm-macros/src/derives/model.rs @@ -183,6 +183,7 @@ impl DeriveModel { } } +/// Method to derive an ActiveModel pub fn expand_derive_model(input: syn::DeriveInput) -> syn::Result { let ident_span = input.ident.span(); diff --git a/sea-orm-macros/src/derives/primary_key.rs b/sea-orm-macros/src/derives/primary_key.rs index f699749f..13c7b5ab 100644 --- a/sea-orm-macros/src/derives/primary_key.rs +++ b/sea-orm-macros/src/derives/primary_key.rs @@ -3,6 +3,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{quote, quote_spanned}; use syn::{Data, DataEnum, Fields, Variant}; +/// Method to derive a Primary Key for a Model using the [PrimaryKeyTrait](sea_orm::PrimaryKeyTrait) pub fn expand_derive_primary_key(ident: Ident, data: Data) -> syn::Result { let variants = match data { syn::Data::Enum(DataEnum { variants, .. }) => variants, diff --git a/sea-orm-macros/src/derives/relation.rs b/sea-orm-macros/src/derives/relation.rs index 4304ab21..ee4f332c 100644 --- a/sea-orm-macros/src/derives/relation.rs +++ b/sea-orm-macros/src/derives/relation.rs @@ -166,6 +166,7 @@ impl DeriveRelation { } } +/// Method to derive a Relation pub fn expand_derive_relation(input: syn::DeriveInput) -> syn::Result { let ident_span = input.ident.span(); diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index cf8c2f3c..bdc52f0f 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -7,6 +7,14 @@ mod attributes; mod derives; mod util; +/// Create an Entity +/// ### Usage +/// ``` +/// use sea_orm::entity::prelude::*; +/// +///#[derive(Copy, Clone, Default, Debug, DeriveEntity)] +///pub struct Entity; +/// ``` #[proc_macro_derive(DeriveEntity, attributes(sea_orm))] pub fn derive_entity(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -15,6 +23,21 @@ pub fn derive_entity(input: TokenStream) -> TokenStream { .into() } +/// This derive macro is the 'almighty' macro which automatically generates +/// Entity, Column, and PrimaryKey from a given Model. +/// ### Usage +/// use sea_orm::entity::prelude::*; +/// +/// #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, FromForm)] +/// #[sea_orm(table_name = "posts")] +/// pub struct Model { +/// #[sea_orm(primary_key)] +/// pub id: i32, +/// pub title: String, +/// #[sea_orm(column_type = "Text")] +/// pub text: String, +/// } +/// ``` #[proc_macro_derive(DeriveEntityModel, attributes(sea_orm))] pub fn derive_entity_model(input: TokenStream) -> TokenStream { let input_ts = input.clone(); @@ -36,6 +59,19 @@ pub fn derive_entity_model(input: TokenStream) -> TokenStream { ts } +/// The DerivePrimaryKey derive macro will implement [PrimaryKeyToColumn] +/// for PrimaryKey which defines tedious mappings between primary keys and columns. +/// The [EnumIter] is also derived, allowing iteration over all enum variants. +/// ### Usage +/// ``` +/// use sea_orm::entity::prelude::*; +/// +/// #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +/// pub enum PrimaryKey { +/// CakeId, +/// FillingId, +/// } +/// ``` #[proc_macro_derive(DerivePrimaryKey)] pub fn derive_primary_key(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); @@ -46,6 +82,19 @@ pub fn derive_primary_key(input: TokenStream) -> TokenStream { } } +/// The DeriveColumn derive macro will implement [ColumnTrait] for Columns. +/// It defines the identifier of each column by implementing Iden and IdenStatic. +/// The EnumIter is also derived, allowing iteration over all enum variants. +/// ### Usage +/// ``` +/// use sea_orm::entity::prelude::*; +/// +/// #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +/// pub enum Column { +/// CakeId, +/// FillingId, +/// } +/// ``` #[proc_macro_derive(DeriveColumn, attributes(sea_orm))] pub fn derive_column(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); @@ -56,6 +105,13 @@ pub fn derive_column(input: TokenStream) -> TokenStream { } } +/// Derive a column if column names are not in snake-case +/// #[derive(Copy, Clone, Debug, EnumIter, DeriveCustomColumn)] +/// pub enum Column { +/// Id, +/// Name, +/// VendorId, +/// } #[proc_macro_derive(DeriveCustomColumn)] pub fn derive_custom_column(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); @@ -66,6 +122,18 @@ pub fn derive_custom_column(input: TokenStream) -> TokenStream { } } +/// The DeriveModel derive macro will implement ModelTrait for Model, +/// which provides setters and getters for all attributes in the mod +/// It also implements FromQueryResult to convert a query result into the corresponding Model. +/// ### Usage +/// +/// ``` +/// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +/// pub struct Model { +/// pub id: i32, +/// pub name: String, +/// } +/// ``` #[proc_macro_derive(DeriveModel, attributes(sea_orm))] pub fn derive_model(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -74,6 +142,19 @@ pub fn derive_model(input: TokenStream) -> TokenStream { .into() } +/// The DeriveActiveModel derive macro will implement ActiveModelTrait for ActiveModel +/// which provides setters and getters for all active values in the active model. +/// ### Usage +/// +/// ``` +/// +/// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +/// pub struct Model { +/// pub id: i32, +/// pub name: String, +/// } +/// +/// ``` #[proc_macro_derive(DeriveActiveModel, attributes(sea_orm))] pub fn derive_active_model(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); @@ -84,6 +165,7 @@ pub fn derive_active_model(input: TokenStream) -> TokenStream { } } +/// FIXME Derive into an active model #[proc_macro_derive(DeriveIntoActiveModel, attributes(sea_orm))] pub fn derive_into_active_model(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); @@ -92,6 +174,18 @@ pub fn derive_into_active_model(input: TokenStream) -> TokenStream { .into() } +/// Models that a user can override +/// ### Usage +/// +/// ``` +/// use sea_orm::entity::prelude::*; +/// +/// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, DeriveActiveModelBehavior,)] +/// pub struct Model { +/// pub id: i32, +/// pub name: String, +/// } +/// ``` #[proc_macro_derive(DeriveActiveModelBehavior)] pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); @@ -102,6 +196,16 @@ pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream { } } +/// Convert a query result into the corresponding Model. +/// ### Usage +/// +/// ``` +/// #[derive(Debug, FromQueryResult)] +/// struct SelectResult { +/// name: String, +/// num_of_fruits: i32, +/// } +/// ``` #[proc_macro_derive(FromQueryResult)] pub fn derive_from_query_result(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); @@ -112,6 +216,25 @@ pub fn derive_from_query_result(input: TokenStream) -> TokenStream { } } +/// The DeriveRelation derive macro will implement RelationTrait for Relation. +/// ### Usage +/// ``` +/// #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +/// pub enum Relation { +/// #[sea_orm( +/// belongs_to = "super::cake::Entity", +/// from = "Column::CakeId", +/// to = "super::cake::Column::Id" +/// )] +/// Cake, +/// #[sea_orm( +/// belongs_to = "super::cake_expanded::Entity", +/// from = "Column::CakeId", +/// to = "super::cake_expanded::Column::Id" +/// )] +/// CakeExpanded, +/// } +/// ``` #[proc_macro_derive(DeriveRelation, attributes(sea_orm))] pub fn derive_relation(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); From 642994a194d6bd469d6c35263215adf2e6198e8d Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Thu, 28 Oct 2021 15:49:28 +0300 Subject: [PATCH 02/18] Documentation for the stream module --- src/database/stream/query.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/database/stream/query.rs b/src/database/stream/query.rs index 8383659a..03a0fc6e 100644 --- a/src/database/stream/query.rs +++ b/src/database/stream/query.rs @@ -12,6 +12,7 @@ use sqlx::{pool::PoolConnection, Executor}; use crate::{DbErr, InnerConnection, QueryResult, Statement}; +/// Creates a stream from a [QueryResult] #[ouroboros::self_referencing] pub struct QueryStream { stmt: Statement, From 06aa9e31754f260290afe62aac02d2238b43e3c0 Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Fri, 29 Oct 2021 10:36:18 +0300 Subject: [PATCH 03/18] Document the macros. Add code examples for the macros --- sea-orm-macros/src/derives/column.rs | 3 +-- sea-orm-macros/src/lib.rs | 5 ++++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/sea-orm-macros/src/derives/column.rs b/sea-orm-macros/src/derives/column.rs index ed035e0b..8e60c560 100644 --- a/sea-orm-macros/src/derives/column.rs +++ b/sea-orm-macros/src/derives/column.rs @@ -3,8 +3,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::{quote, quote_spanned}; use syn::{punctuated::Punctuated, token::Comma, Data, DataEnum, Fields, Lit, Meta, Variant}; -/// FIXME -/// Method to derive default column names from a &str +/// Derive a Column name for an enum type pub fn impl_default_as_str(ident: &Ident, data: &Data) -> syn::Result { let variants = match data { syn::Data::Enum(DataEnum { variants, .. }) => variants, diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index bdc52f0f..87ac07d0 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -106,12 +106,15 @@ pub fn derive_column(input: TokenStream) -> TokenStream { } /// Derive a column if column names are not in snake-case +/// ### Usage +/// ``` /// #[derive(Copy, Clone, Debug, EnumIter, DeriveCustomColumn)] /// pub enum Column { /// Id, /// Name, /// VendorId, /// } +/// ``` #[proc_macro_derive(DeriveCustomColumn)] pub fn derive_custom_column(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); @@ -165,7 +168,7 @@ pub fn derive_active_model(input: TokenStream) -> TokenStream { } } -/// FIXME Derive into an active model +/// Derive into an active model #[proc_macro_derive(DeriveIntoActiveModel, attributes(sea_orm))] pub fn derive_into_active_model(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); From 91b9e542af347a38bf43461d9790ae953bedb6ff Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Fri, 29 Oct 2021 10:37:10 +0300 Subject: [PATCH 04/18] Documetation for the database modeule --- src/database/connection.rs | 12 ++++++++++++ src/database/db_connection.rs | 21 ++++++++++++++++++++- src/database/mock.rs | 18 ++++++++++++++++++ src/database/mod.rs | 11 +++++++++++ src/database/statement.rs | 9 +++++++++ src/database/transaction.rs | 9 +++++++++ 6 files changed, 79 insertions(+), 1 deletion(-) diff --git a/src/database/connection.rs b/src/database/connection.rs index d90c72a9..be47f9da 100644 --- a/src/database/connection.rs +++ b/src/database/connection.rs @@ -4,23 +4,34 @@ use crate::{ use futures::Stream; use std::{future::Future, pin::Pin}; +/// Creates constraints for any structure that wants to create a database connection +/// and execute SQL statements #[async_trait::async_trait] pub trait ConnectionTrait<'a>: Sync { + /// Create a stream for the [QueryResult] type Stream: Stream>; + /// Fetch the database backend as specified in [DbBackend]. + /// This depends on feature flags enabled. fn get_database_backend(&self) -> DbBackend; + /// Execute a [Statement] async fn execute(&self, stmt: Statement) -> Result; + /// Execute a [Statement] and return a query async fn query_one(&self, stmt: Statement) -> Result, DbErr>; + /// Execute a [Statement] and return a collection Vec<[QueryResult]> on success async fn query_all(&self, stmt: Statement) -> Result, DbErr>; + /// Execute a [Statement] and return a stream of results fn stream( &'a self, stmt: Statement, ) -> Pin> + 'a>>; + /// Execute SQL `BEGIN` transaction. + /// Returns a Transaction that can be committed or rolled back async fn begin(&self) -> Result; /// Execute the function inside a transaction. @@ -34,6 +45,7 @@ pub trait ConnectionTrait<'a>: Sync { T: Send, E: std::error::Error + Send; + /// Check if the connection is a test connection for the Mock database fn is_mock_connection(&self) -> bool { false } diff --git a/src/database/db_connection.rs b/src/database/db_connection.rs index 6b8e3356..681903dd 100644 --- a/src/database/db_connection.rs +++ b/src/database/db_connection.rs @@ -12,30 +12,43 @@ use sqlx::pool::PoolConnection; #[cfg(feature = "mock")] use std::sync::Arc; +/// Handle a database connection depending on the backend +/// enabled by the feature flags. This creates a database pool. #[cfg_attr(not(feature = "mock"), derive(Clone))] pub enum DatabaseConnection { + /// Create a MYSQL database connection and pool #[cfg(feature = "sqlx-mysql")] SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection), + /// Create a PostgreSQL database connection and pool #[cfg(feature = "sqlx-postgres")] SqlxPostgresPoolConnection(crate::SqlxPostgresPoolConnection), + /// Create a SQLite database connection and pool #[cfg(feature = "sqlx-sqlite")] SqlxSqlitePoolConnection(crate::SqlxSqlitePoolConnection), + /// Create a Mock database connection useful for testing #[cfg(feature = "mock")] MockDatabaseConnection(Arc), + /// The connection to the database has been severed Disconnected, } +/// The same as a [DatabaseConnection] pub type DbConn = DatabaseConnection; +/// The type of database backend for real world databases. +/// This is enabled by feature flags as specified in the crate documentation #[derive(Debug, Copy, Clone, PartialEq)] pub enum DatabaseBackend { + /// A MySQL backend MySql, + /// A PostgreSQL backend Postgres, + /// A SQLite backend Sqlite, } +/// The same as [DatabaseBackend] just shorter :) pub type DbBackend = DatabaseBackend; - pub(crate) enum InnerConnection { #[cfg(feature = "sqlx-mysql")] MySql(PoolConnection), @@ -209,6 +222,7 @@ impl<'a> ConnectionTrait<'a> for DatabaseConnection { #[cfg(feature = "mock")] impl DatabaseConnection { + /// Generate a database connection for testing the Mock database pub fn as_mock_connection(&self) -> &crate::MockDatabaseConnection { match self { DatabaseConnection::MockDatabaseConnection(mock_conn) => mock_conn, @@ -216,6 +230,7 @@ impl DatabaseConnection { } } + /// Get the transaction log as a collection Vec<[crate::Transaction]> pub fn into_transaction_log(self) -> Vec { let mut mocker = self.as_mock_connection().get_mocker_mutex().lock().unwrap(); mocker.drain_transaction_log() @@ -223,6 +238,8 @@ impl DatabaseConnection { } impl DbBackend { + /// Check if the URI is the same as the specified database backend. + /// Returns true if they match. pub fn is_prefix_of(self, base_url: &str) -> bool { let base_url_parsed = Url::parse(base_url).unwrap(); match self { @@ -234,6 +251,7 @@ impl DbBackend { } } + /// Build an SQL [Statement] pub fn build(&self, statement: &S) -> Statement where S: StatementBuilder, @@ -241,6 +259,7 @@ impl DbBackend { statement.build(self) } + /// A helper for building SQL queries pub fn get_query_builder(&self) -> Box { match self { Self::MySql => Box::new(MysqlQueryBuilder), diff --git a/src/database/mock.rs b/src/database/mock.rs index 44522944..74ffd8bf 100644 --- a/src/database/mock.rs +++ b/src/database/mock.rs @@ -6,6 +6,7 @@ use crate::{ use sea_query::{Value, ValueType, Values}; use std::{collections::BTreeMap, sync::Arc}; +/// Defines a Mock database suitable for testing #[derive(Debug)] pub struct MockDatabase { db_backend: DbBackend, @@ -15,33 +16,44 @@ pub struct MockDatabase { query_results: Vec>, } +/// Defines the results obtained from a [MockDatabase] #[derive(Clone, Debug, Default)] pub struct MockExecResult { + /// The last inserted id on auto-increment pub last_insert_id: u64, + /// The number of rows affected by the database operation pub rows_affected: u64, } +/// Defines the structure of a test Row for the [MockDatabase] +/// which is just a [BTreeMap]<[String], [Value]> #[derive(Clone, Debug)] pub struct MockRow { values: BTreeMap, } +/// A trait to get a [MockRow] from a type useful for testing in the [MockDatabase] pub trait IntoMockRow { + /// The method to perform this operation fn into_mock_row(self) -> MockRow; } +/// Defines a transaction that is has not been committed #[derive(Debug)] pub struct OpenTransaction { stmts: Vec, transaction_depth: usize, } +/// Defines a database transaction as it holds a Vec<[Statement]> #[derive(Debug, Clone, PartialEq)] pub struct Transaction { stmts: Vec, } impl MockDatabase { + /// Instantiate a mock database with a [DbBackend] to simulate real + /// world SQL databases pub fn new(db_backend: DbBackend) -> Self { Self { db_backend, @@ -52,15 +64,18 @@ impl MockDatabase { } } + /// Create a database connection pub fn into_connection(self) -> DatabaseConnection { DatabaseConnection::MockDatabaseConnection(Arc::new(MockDatabaseConnection::new(self))) } + /// Add the [MockExecResult]s to the `exec_results` field for `Self` pub fn append_exec_results(mut self, mut vec: Vec) -> Self { self.exec_results.append(&mut vec); self } + /// Add the [MockExecResult]s to the `exec_results` field for `Self` pub fn append_query_results(mut self, vec: Vec>) -> Self where T: IntoMockRow, @@ -150,6 +165,7 @@ impl MockDatabaseTrait for MockDatabase { } impl MockRow { + /// Try to get the values of a [MockRow] and fail gracefully on error pub fn try_get(&self, col: &str) -> Result where T: ValueType, @@ -157,6 +173,7 @@ impl MockRow { T::try_from(self.values.get(col).unwrap().clone()).map_err(|e| DbErr::Query(e.to_string())) } + /// An iterator over the keys and values of a mock row pub fn into_column_value_tuples(self) -> impl Iterator { self.values.into_iter() } @@ -190,6 +207,7 @@ impl IntoMockRow for BTreeMap<&str, Value> { } impl Transaction { + /// Get the [Value]s from s raw SQL statement depending on the [DatabaseBackend](crate::DatabaseBackend) pub fn from_sql_and_values(db_backend: DbBackend, sql: &str, values: I) -> Self where I: IntoIterator, diff --git a/src/database/mod.rs b/src/database/mod.rs index 6a06a48c..d6cf5d68 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -18,20 +18,30 @@ pub use transaction::*; use crate::DbErr; +/// Defines a database #[derive(Debug, Default)] pub struct Database; +/// Defines the configuration options of a database #[derive(Debug)] pub struct ConnectOptions { + /// The URI of the database pub(crate) url: String, + /// Maximum number of connections for a pool pub(crate) max_connections: Option, + /// Minimum number of connections for a pool pub(crate) min_connections: Option, + /// The connection timeout for a packet connection pub(crate) connect_timeout: Option, + /// Maximum idle time for a particular connection to prevent + /// network resource exhaustion pub(crate) idle_timeout: Option, + /// Enables or disables logging pub(crate) sqlx_logging: bool, } impl Database { + /// Method to create a [DatabaseConnection] on a database pub async fn connect(opt: C) -> Result where C: Into, @@ -80,6 +90,7 @@ impl From for ConnectOptions { } impl ConnectOptions { + /// Create new [ConnectOptions] for a [Database] by passing in a URI string pub fn new(url: String) -> Self { Self { url, diff --git a/src/database/statement.rs b/src/database/statement.rs index 12b07487..1c970ea1 100644 --- a/src/database/statement.rs +++ b/src/database/statement.rs @@ -3,18 +3,25 @@ use sea_query::{inject_parameters, MysqlQueryBuilder, PostgresQueryBuilder, Sqli pub use sea_query::{Value, Values}; use std::fmt; +/// Defines an SQL statement #[derive(Debug, Clone, PartialEq)] pub struct Statement { + /// The SQL query pub sql: String, + /// The values for the SQL statement pub values: Option, + /// The database backend to use pub db_backend: DbBackend, } +/// Constraints for building a [Statement] pub trait StatementBuilder { + /// Method to call in order to build a [Statement] fn build(&self, db_backend: &DbBackend) -> Statement; } impl Statement { + /// Create a [Statement] from a [crate::DatabaseBackend] and a raw SQL statement pub fn from_string(db_backend: DbBackend, stmt: String) -> Statement { Statement { sql: stmt, @@ -23,6 +30,8 @@ impl Statement { } } + /// Create a SQL statement from a [crate::DatabaseBackend], a + /// raw SQL statement and defined values pub fn from_sql_and_values(db_backend: DbBackend, sql: &str, values: I) -> Self where I: IntoIterator, diff --git a/src/database/transaction.rs b/src/database/transaction.rs index d7bbc058..f4a1b678 100644 --- a/src/database/transaction.rs +++ b/src/database/transaction.rs @@ -10,6 +10,8 @@ use sqlx::{pool::PoolConnection, TransactionManager}; use std::{future::Future, pin::Pin, sync::Arc}; // a Transaction is just a sugar for a connection where START TRANSACTION has been executed +/// Defines a database transaction, whether it is an open transaction and the type of +/// backend to use pub struct DatabaseTransaction { conn: Arc>, backend: DbBackend, @@ -100,6 +102,8 @@ impl DatabaseTransaction { Ok(res) } + /// Runs a transaction to completion returning an rolling back the transaction on + /// encountering an error if it fails pub(crate) async fn run(self, callback: F) -> Result> where F: for<'b> FnOnce( @@ -120,6 +124,7 @@ impl DatabaseTransaction { res } + /// Commit a transaction atomically pub async fn commit(mut self) -> Result<(), DbErr> { self.open = false; match *self.conn.lock().await { @@ -149,6 +154,7 @@ impl DatabaseTransaction { Ok(()) } + /// rolls back a transaction in case error are encountered during the operation pub async fn rollback(mut self) -> Result<(), DbErr> { self.open = false; match *self.conn.lock().await { @@ -343,12 +349,15 @@ impl<'a> ConnectionTrait<'a> for DatabaseTransaction { } } +/// Defines errors for handling transaction failures #[derive(Debug)] pub enum TransactionError where E: std::error::Error, { + /// A Database connection error Connection(DbErr), + /// An error occurring when doing database transactions Transaction(E), } From 69d5c701eed09cc65970113cdd8701f05975945e Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Fri, 29 Oct 2021 10:37:54 +0300 Subject: [PATCH 05/18] Documetation for the schema module --- src/schema/entity.rs | 1 + src/schema/mod.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/schema/entity.rs b/src/schema/entity.rs index a95b7047..51d5994e 100644 --- a/src/schema/entity.rs +++ b/src/schema/entity.rs @@ -5,6 +5,7 @@ use crate::{ use sea_query::{ColumnDef, ForeignKeyCreateStatement, Iden, Index, TableCreateStatement}; impl Schema { + /// Creates a table from an Entity. See [TableCreateStatement] for more details pub fn create_table_from_entity(entity: E) -> TableCreateStatement where E: EntityTrait, diff --git a/src/schema/mod.rs b/src/schema/mod.rs index 1d05e679..3a5ce734 100644 --- a/src/schema/mod.rs +++ b/src/schema/mod.rs @@ -1,4 +1,5 @@ mod entity; +/// This structure defines a schema for a table #[derive(Debug)] pub struct Schema; From a9b6f8cc83176634779f4ebf7e49a4257c76870f Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Fri, 29 Oct 2021 10:38:30 +0300 Subject: [PATCH 06/18] Documetation for the query module --- src/query/combine.rs | 3 +++ src/query/delete.rs | 3 +++ src/query/helper.rs | 10 ++++++++++ src/query/insert.rs | 3 +++ src/query/select.rs | 5 +++++ src/query/traits.rs | 2 ++ src/query/update.rs | 5 +++++ src/query/util.rs | 4 ++++ 8 files changed, 35 insertions(+) diff --git a/src/query/combine.rs b/src/query/combine.rs index c03906ea..a3dcc275 100644 --- a/src/query/combine.rs +++ b/src/query/combine.rs @@ -7,6 +7,7 @@ use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatemen macro_rules! select_def { ( $ident: ident, $str: expr ) => { + /// Implements the traits [Iden] and [IdenStatic] for a type #[derive(Debug, Clone, Copy)] pub struct $ident; @@ -54,6 +55,7 @@ where self } + /// Selects and Entity and returns it together with the Entity from `Self` pub fn select_also(mut self, _: F) -> SelectTwo where F: EntityTrait, @@ -62,6 +64,7 @@ where SelectTwo::new(self.into_query()) } + /// Makes a SELECT operation in conjunction to another relation pub fn select_with(mut self, _: F) -> SelectTwoMany where F: EntityTrait, diff --git a/src/query/delete.rs b/src/query/delete.rs index e5cefdef..c7df0c7d 100644 --- a/src/query/delete.rs +++ b/src/query/delete.rs @@ -5,9 +5,11 @@ use crate::{ use core::marker::PhantomData; use sea_query::{DeleteStatement, IntoIden}; +/// Defines the structure for a delete operation #[derive(Clone, Debug)] pub struct Delete; +/// Perform a delete operation on a model #[derive(Clone, Debug)] pub struct DeleteOne where @@ -17,6 +19,7 @@ where pub(crate) model: A, } +/// Perform a delete operation on multiple models #[derive(Clone, Debug)] pub struct DeleteMany where diff --git a/src/query/helper.rs b/src/query/helper.rs index bf762fc9..46f8c895 100644 --- a/src/query/helper.rs +++ b/src/query/helper.rs @@ -11,9 +11,12 @@ pub use sea_query::{Condition, ConditionalStatement, DynIden, JoinType, Order, O // LINT: when the column does not appear in tables selected from // LINT: when there is a group by clause, but some columns don't have aggregate functions // LINT: when the join table or column does not exists +/// Constraints for any type that needs to perform select statements on a Model pub trait QuerySelect: Sized { + #[allow(missing_docs)] type QueryStatement; + /// Add the select SQL statement fn query(&mut self) -> &mut SelectStatement; /// Clear the selection list @@ -164,9 +167,12 @@ pub trait QuerySelect: Sized { } // LINT: when the column does not appear in tables selected from +/// Performs ORDER BY operations pub trait QueryOrder: Sized { + #[allow(missing_docs)] type QueryStatement: OrderedStatement; + /// Add the query to perform an ORDER BY operation fn query(&mut self) -> &mut SelectStatement; /// Add an order_by expression @@ -234,9 +240,12 @@ pub trait QueryOrder: Sized { } // LINT: when the column does not appear in tables selected from +/// Perform a FILTER opertation on a statement pub trait QueryFilter: Sized { + #[allow(missing_docs)] type QueryStatement: ConditionalStatement; + /// Add the query to perform a FILTER on fn query(&mut self) -> &mut Self::QueryStatement; /// Add an AND WHERE expression @@ -372,6 +381,7 @@ pub trait QueryFilter: Sized { self } + /// Perform a check to determine table belongs to a Model through it's name alias fn belongs_to_tbl_alias(mut self, model: &M, tbl_alias: &str) -> Self where M: ModelTrait, diff --git a/src/query/insert.rs b/src/query/insert.rs index 5e504a0c..f862ccba 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -5,6 +5,7 @@ use crate::{ use core::marker::PhantomData; use sea_query::{InsertStatement, ValueTuple}; +/// Performs INSERT operations on a ActiveModel #[derive(Debug)] pub struct Insert where @@ -106,6 +107,7 @@ where Self::new().add_many(models) } + /// Add a Model to Self #[allow(clippy::should_implement_trait)] pub fn add(mut self, m: M) -> Self where @@ -139,6 +141,7 @@ where self } + /// Add many Models to Self pub fn add_many(mut self, models: I) -> Self where M: IntoActiveModel, diff --git a/src/query/select.rs b/src/query/select.rs index 1b0c93c3..adfb07db 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -4,6 +4,7 @@ use core::marker::PhantomData; pub use sea_query::JoinType; use sea_query::{DynIden, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr}; +/// Defines a structure to perform select operations #[derive(Clone, Debug)] pub struct Select where @@ -13,6 +14,7 @@ where pub(crate) entity: PhantomData, } +/// Defines a structure to perform a SELECT operation on two Models #[derive(Clone, Debug)] pub struct SelectTwo where @@ -23,6 +25,7 @@ where pub(crate) entity: PhantomData<(E, F)>, } +/// Defines a structure to perform a SELECT operation on many Models #[derive(Clone, Debug)] pub struct SelectTwoMany where @@ -33,7 +36,9 @@ where pub(crate) entity: PhantomData<(E, F)>, } +/// Performs a conversion to [SimpleExpr] pub trait IntoSimpleExpr { + /// Method to perform the conversion fn into_simple_expr(self) -> SimpleExpr; } diff --git a/src/query/traits.rs b/src/query/traits.rs index f0045f52..0517cc64 100644 --- a/src/query/traits.rs +++ b/src/query/traits.rs @@ -1,7 +1,9 @@ use crate::{DbBackend, Statement}; use sea_query::QueryStatementBuilder; +/// Enforces a set of constraints to any type performing queries on a Model or ActiveModel pub trait QueryTrait { + /// Constrain the QueryStatement to [QueryStatementBuilder] trait type QueryStatement: QueryStatementBuilder; /// Get a mutable ref to the query builder diff --git a/src/query/update.rs b/src/query/update.rs index fec7757c..177d9d9e 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -5,9 +5,11 @@ use crate::{ use core::marker::PhantomData; use sea_query::{IntoIden, SimpleExpr, UpdateStatement}; +/// Defines a structure to perform UPDATE query operations on a ActiveModel #[derive(Clone, Debug)] pub struct Update; +/// Defines an UPDATE operation on one ActiveModel #[derive(Clone, Debug)] pub struct UpdateOne where @@ -17,6 +19,7 @@ where pub(crate) model: A, } +/// Defines an UPDATE operation on multiple ActiveModels #[derive(Clone, Debug)] pub struct UpdateMany where @@ -177,6 +180,7 @@ impl UpdateMany where E: EntityTrait, { + /// Add the models to update to Self pub fn set(mut self, model: A) -> Self where A: ActiveModelTrait, @@ -190,6 +194,7 @@ where self } + /// Creates a [SimpleExpr] from a column pub fn col_expr(mut self, col: T, expr: SimpleExpr) -> Self where T: IntoIden, diff --git a/src/query/util.rs b/src/query/util.rs index 5fc88950..b23804f2 100644 --- a/src/query/util.rs +++ b/src/query/util.rs @@ -1,8 +1,11 @@ use crate::{database::*, QueryTrait, Statement}; +/// This structure provides debug capabilities #[derive(Debug)] pub struct DebugQuery<'a, Q, T> { + /// The query to debug pub query: &'a Q, + /// The value of the query pub value: T, } @@ -12,6 +15,7 @@ macro_rules! debug_query_build { where Q: QueryTrait, { + /// This macro builds a [Statement] when invoked pub fn build(&self) -> Statement { let func = $db_expr; let db_backend = func(self); From 817e9bdd2313658c78e4205391768ffd9fec4689 Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Fri, 29 Oct 2021 10:38:55 +0300 Subject: [PATCH 07/18] Documetation for the executor module --- src/executor/delete.rs | 7 +++++++ src/executor/execute.rs | 10 ++++++++++ src/executor/insert.rs | 6 ++++++ src/executor/paginator.rs | 2 ++ src/executor/query.rs | 13 ++++++++++++- src/executor/select.rs | 34 +++++++++++++++++++++++++++++++++- src/executor/update.rs | 8 ++++++++ 7 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/executor/delete.rs b/src/executor/delete.rs index c4a8c7de..7577b976 100644 --- a/src/executor/delete.rs +++ b/src/executor/delete.rs @@ -4,13 +4,16 @@ use crate::{ use sea_query::DeleteStatement; use std::future::Future; +/// Handles DELETE operations in a ActiveModel using [DeleteStatement] #[derive(Clone, Debug)] pub struct Deleter { query: DeleteStatement, } +/// The result of a DELETE operation #[derive(Clone, Debug)] pub struct DeleteResult { + /// The number of rows affected by the DELETE operation pub rows_affected: u64, } @@ -18,6 +21,7 @@ impl<'a, A: 'a> DeleteOne where A: ActiveModelTrait, { + /// Execute a DELETE operation on one ActiveModel pub fn exec(self, db: &'a C) -> impl Future> + '_ where C: ConnectionTrait<'a>, @@ -31,6 +35,7 @@ impl<'a, E> DeleteMany where E: EntityTrait, { + /// Execute a DELETE operation on many ActiveModels pub fn exec(self, db: &'a C) -> impl Future> + '_ where C: ConnectionTrait<'a>, @@ -41,10 +46,12 @@ where } impl Deleter { + /// Instantiate a new [Deleter] by passing it a [DeleteStatement] pub fn new(query: DeleteStatement) -> Self { Self { query } } + /// Execute a DELETE operation pub fn exec<'a, C>(self, db: &'a C) -> impl Future> + '_ where C: ConnectionTrait<'a>, diff --git a/src/executor/execute.rs b/src/executor/execute.rs index 46ba2d69..f3168180 100644 --- a/src/executor/execute.rs +++ b/src/executor/execute.rs @@ -1,16 +1,24 @@ +/// Defines the result of executing an operation #[derive(Debug)] pub struct ExecResult { + /// The type of result from the execution depending on the feature flag enabled + /// to choose a database backend pub(crate) result: ExecResultHolder, } +/// Holds a result depending on the database backend chosen by the feature flag #[derive(Debug)] pub(crate) enum ExecResultHolder { + /// Holds the result of executing an operation on a MySQL database #[cfg(feature = "sqlx-mysql")] SqlxMySql(sqlx::mysql::MySqlQueryResult), + /// Holds the result of executing an operation on a PostgreSQL database #[cfg(feature = "sqlx-postgres")] SqlxPostgres(sqlx::postgres::PgQueryResult), + /// Holds the result of executing an operation on a SQLite database #[cfg(feature = "sqlx-sqlite")] SqlxSqlite(sqlx::sqlite::SqliteQueryResult), + /// Holds the result of executing an operation on the Mock database #[cfg(feature = "mock")] Mock(crate::MockExecResult), } @@ -18,6 +26,7 @@ pub(crate) enum ExecResultHolder { // ExecResult // impl ExecResult { + /// Get the last id after `AUTOINCREMENT` is done on the primary key pub fn last_insert_id(&self) -> u64 { match &self.result { #[cfg(feature = "sqlx-mysql")] @@ -40,6 +49,7 @@ impl ExecResult { } } + /// Get the number of rows affedted by the operation pub fn rows_affected(&self) -> u64 { match &self.result { #[cfg(feature = "sqlx-mysql")] diff --git a/src/executor/insert.rs b/src/executor/insert.rs index 9e186ccd..64b6c145 100644 --- a/src/executor/insert.rs +++ b/src/executor/insert.rs @@ -5,6 +5,7 @@ use crate::{ use sea_query::{FromValueTuple, InsertStatement, ValueTuple}; use std::{future::Future, marker::PhantomData}; +/// Defines a structure to perform INSERT operations in an ActiveModel #[derive(Debug)] pub struct Inserter where @@ -15,11 +16,13 @@ where model: PhantomData, } +/// The result of an INSERT operation on an ActiveModel #[derive(Debug)] pub struct InsertResult where A: ActiveModelTrait, { + /// The id performed when AUTOINCREMENT was performed on the PrimaryKey pub last_insert_id: <<::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType, } @@ -27,6 +30,7 @@ impl Insert where A: ActiveModelTrait, { + /// Execute an insert operation #[allow(unused_mut)] pub fn exec<'a, C>(self, db: &'a C) -> impl Future, DbErr>> + '_ where @@ -53,6 +57,7 @@ impl Inserter where A: ActiveModelTrait, { + /// Instantiate a new insert operation pub fn new(primary_key: Option, query: InsertStatement) -> Self { Self { primary_key, @@ -61,6 +66,7 @@ where } } + /// Execute an insert operation pub fn exec<'a, C>(self, db: &'a C) -> impl Future, DbErr>> + '_ where C: ConnectionTrait<'a>, diff --git a/src/executor/paginator.rs b/src/executor/paginator.rs index 28f8574b..fd8e822c 100644 --- a/src/executor/paginator.rs +++ b/src/executor/paginator.rs @@ -4,8 +4,10 @@ use futures::Stream; use sea_query::{Alias, Expr, SelectStatement}; use std::{marker::PhantomData, pin::Pin}; +/// Pin a Model so that stream operations can be performed on the model pub type PinBoxStream<'db, Item> = Pin + 'db>>; +/// Defined a structure to handle pagination of a result from a query operation on a Model #[derive(Clone, Debug)] pub struct Paginator<'db, C, S> where diff --git a/src/executor/query.rs b/src/executor/query.rs index a164e911..96455945 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -3,6 +3,7 @@ use crate::debug_print; use crate::{DbErr, SelectGetableValue, SelectorRaw, Statement}; use std::fmt; +/// Defines the result of a query operation on a Model #[derive(Debug)] pub struct QueryResult { pub(crate) row: QueryResultRow, @@ -19,13 +20,18 @@ pub(crate) enum QueryResultRow { Mock(crate::MockRow), } +/// Constrain any type trying to get a Row in a database pub trait TryGetable: Sized { + /// Ensure the type implements this method fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result; } +/// An error from trying to get a row from a Model #[derive(Debug)] pub enum TryGetError { + /// A database error was encountered as defined in [crate::DbErr] DbErr(DbErr), + /// A null value was encountered Null, } @@ -41,6 +47,7 @@ impl From for DbErr { // QueryResult // impl QueryResult { + /// Get a Row from a Column pub fn try_get(&self, pre: &str, col: &str) -> Result where T: TryGetable, @@ -48,6 +55,7 @@ impl QueryResult { Ok(T::try_get(self, pre, col)?) } + /// Perform query operations on multiple Columns pub fn try_get_many(&self, pre: &str, cols: &[String]) -> Result where T: TryGetableMany, @@ -306,7 +314,9 @@ try_getable_all!(uuid::Uuid); // TryGetableMany // +/// Perform a query on multiple columns pub trait TryGetableMany: Sized { + /// THe method to perform a query on multiple columns fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result; /// ``` @@ -453,8 +463,9 @@ fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), Try } // TryFromU64 // - +/// Try to convert a type to a u64 pub trait TryFromU64: Sized { + /// The method to convert the type to a u64 fn try_from_u64(n: u64) -> Result; } diff --git a/src/executor/select.rs b/src/executor/select.rs index 78786b76..5acdae02 100644 --- a/src/executor/select.rs +++ b/src/executor/select.rs @@ -11,6 +11,7 @@ use std::pin::Pin; #[cfg(feature = "with-json")] use crate::JsonValue; +/// Defines a type to do `SELECT` operations though a [SelectStatement] on a Model #[derive(Clone, Debug)] pub struct Selector where @@ -20,6 +21,7 @@ where selector: S, } +/// Performs a raw `SELECT` operation on a model #[derive(Clone, Debug)] pub struct SelectorRaw where @@ -29,12 +31,16 @@ where selector: S, } +/// Used to enforce constraints on any type that wants to perform SELECT queries pub trait SelectorTrait { + #[allow(missing_docs)] type Item: Sized; + /// The method to perform a query on a Model fn from_raw_query_result(res: QueryResult) -> Result; } +/// Perform an operation on an entity that can yield a Value #[derive(Debug)] pub struct SelectGetableValue where @@ -45,6 +51,7 @@ where model: PhantomData, } +/// Defines a type to get a Model #[derive(Debug)] pub struct SelectModel where @@ -53,6 +60,7 @@ where model: PhantomData, } +/// Defines a type to get two Modelss #[derive(Clone, Debug)] pub struct SelectTwoModel where @@ -105,6 +113,7 @@ impl Select where E: EntityTrait, { + /// Perform a Select operation on a Model using a [Statement] #[allow(clippy::wrong_self_convention)] pub fn from_raw_sql(self, stmt: Statement) -> SelectorRaw> { SelectorRaw { @@ -113,6 +122,7 @@ where } } + /// Return a [Selector] from `Self` that wraps a [SelectModel] pub fn into_model(self) -> Selector> where M: FromQueryResult, @@ -123,6 +133,7 @@ where } } + /// Get a selectable Model as a [JsonValue] for SQL JSON operations #[cfg(feature = "with-json")] pub fn into_json(self) -> Selector> { Selector { @@ -239,6 +250,7 @@ where Selector::>::with_columns(self.query) } + /// Get one Model from a SELECT operation pub async fn one<'a, C>(self, db: &C) -> Result, DbErr> where C: ConnectionTrait<'a>, @@ -246,6 +258,7 @@ where self.into_model().one(db).await } + /// Get all the Models from a SELECT operation pub async fn all<'a, C>(self, db: &C) -> Result, DbErr> where C: ConnectionTrait<'a>, @@ -253,6 +266,7 @@ where self.into_model().all(db).await } + /// Stream the results of a SELECT operation on a Model pub async fn stream<'a: 'b, 'b, C>( self, db: &'a C, @@ -263,6 +277,7 @@ where self.into_model().stream(db).await } + /// Paginate the results of a SELECT operation on a Model pub fn paginate<'a, C>( self, db: &'a C, @@ -274,6 +289,7 @@ where self.into_model().paginate(db, page_size) } + /// Perform a `COUNT` operation on a items on a Model using pagination pub async fn count<'a, C>(self, db: &'a C) -> Result where C: ConnectionTrait<'a>, @@ -287,6 +303,7 @@ where E: EntityTrait, F: EntityTrait, { + /// Perform a conversion into a [SelectTwoModel] pub fn into_model(self) -> Selector> where M: FromQueryResult, @@ -298,6 +315,7 @@ where } } + /// Convert the Models into JsonValue #[cfg(feature = "with-json")] pub fn into_json(self) -> Selector> { Selector { @@ -306,6 +324,7 @@ where } } + /// Get one Model from a Select operation pub async fn one<'a, C>(self, db: &C) -> Result)>, DbErr> where C: ConnectionTrait<'a>, @@ -313,6 +332,7 @@ where self.into_model().one(db).await } + /// Get all Models from a Select operation pub async fn all<'a, C>(self, db: &C) -> Result)>, DbErr> where C: ConnectionTrait<'a>, @@ -320,6 +340,7 @@ where self.into_model().all(db).await } + /// Stream the results of a Select operation on a Model pub async fn stream<'a: 'b, 'b, C>( self, db: &'a C, @@ -330,6 +351,7 @@ where self.into_model().stream(db).await } + /// Paginate the results of a select operation on two models pub fn paginate<'a, C>( self, db: &'a C, @@ -341,6 +363,7 @@ where self.into_model().paginate(db, page_size) } + /// Perform a count on the paginated results pub async fn count<'a, C>(self, db: &'a C) -> Result where C: ConnectionTrait<'a>, @@ -354,6 +377,7 @@ where E: EntityTrait, F: EntityTrait, { + /// Performs a conversion to [Selector] fn into_model(self) -> Selector> where M: FromQueryResult, @@ -365,6 +389,7 @@ where } } + /// Convert the results to JSON #[cfg(feature = "with-json")] pub fn into_json(self) -> Selector> { Selector { @@ -373,6 +398,7 @@ where } } + /// Select one Model pub async fn one<'a, C>(self, db: &C) -> Result)>, DbErr> where C: ConnectionTrait<'a>, @@ -380,6 +406,7 @@ where self.into_model().one(db).await } + /// Stream the result of the operation pub async fn stream<'a: 'b, 'b, C>( self, db: &'a C, @@ -390,6 +417,7 @@ where self.into_model().stream(db).await } + /// Get all the Models from the select operation pub async fn all<'a, C>(self, db: &C) -> Result)>, DbErr> where C: ConnectionTrait<'a>, @@ -428,6 +456,7 @@ where } } + /// Get a Model from a Select operation pub async fn one<'a, C>(mut self, db: &C) -> Result, DbErr> where C: ConnectionTrait<'a>, @@ -441,6 +470,7 @@ where } } + /// Get all results from a Select operation pub async fn all<'a, C>(self, db: &C) -> Result, DbErr> where C: ConnectionTrait<'a>, @@ -454,6 +484,7 @@ where Ok(models) } + /// Stream the results of the operation pub async fn stream<'a: 'b, 'b, C>( self, db: &'a C, @@ -469,6 +500,7 @@ where }))) } + /// Paginate the result of a select operation on a Model pub fn paginate<'a, C>(self, db: &'a C, page_size: usize) -> Paginator<'a, C, S> where C: ConnectionTrait<'a>, @@ -499,7 +531,7 @@ where } } - /// Create `SelectorRaw` from Statment and columns. Executing this `SelectorRaw` will + /// Create `SelectorRaw` from Statement and columns. Executing this `SelectorRaw` will /// return a type `T` which implement `TryGetableMany`. pub fn with_columns(stmt: Statement) -> SelectorRaw> where diff --git a/src/executor/update.rs b/src/executor/update.rs index c228730b..402f29ac 100644 --- a/src/executor/update.rs +++ b/src/executor/update.rs @@ -4,14 +4,17 @@ use crate::{ use sea_query::UpdateStatement; use std::future::Future; +/// Defines an update operation #[derive(Clone, Debug)] pub struct Updater { query: UpdateStatement, check_record_exists: bool, } +/// The result of an update operation on an ActiveModel #[derive(Clone, Debug, PartialEq)] pub struct UpdateResult { + /// The rows affected by the update operation pub rows_affected: u64, } @@ -19,6 +22,7 @@ impl<'a, A: 'a> UpdateOne where A: ActiveModelTrait, { + /// Execute an update operation on an ActiveModel pub async fn exec<'b, C>(self, db: &'b C) -> Result where C: ConnectionTrait<'b>, @@ -32,6 +36,7 @@ impl<'a, E> UpdateMany where E: EntityTrait, { + /// Execute an update operation on multiple ActiveModels pub fn exec(self, db: &'a C) -> impl Future> + '_ where C: ConnectionTrait<'a>, @@ -42,6 +47,7 @@ where } impl Updater { + /// Instantiate an update using an [UpdateStatement] pub fn new(query: UpdateStatement) -> Self { Self { query, @@ -49,11 +55,13 @@ impl Updater { } } + /// Check if a record exists on the ActiveModel to perform the update operation on pub fn check_record_exists(mut self) -> Self { self.check_record_exists = true; self } + /// Execute an update operation pub fn exec<'a, C>(self, db: &'a C) -> impl Future> + '_ where C: ConnectionTrait<'a>, From db098d1a03ac296e720adbfbc428d8f16eca6603 Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Fri, 29 Oct 2021 10:39:16 +0300 Subject: [PATCH 08/18] Documetation for the entity module --- src/entity/active_model.rs | 33 ++++++++++++++++++++++++++++++ src/entity/base_entity.rs | 14 +++++++++++++ src/entity/column.rs | 41 ++++++++++++++++++++++++++++++++++++++ src/entity/identity.rs | 8 ++++++++ src/entity/link.rs | 6 ++++++ src/entity/mod.rs | 1 + src/entity/model.rs | 10 ++++++++++ src/entity/prelude.rs | 1 + src/entity/primary_key.rs | 7 +++++++ src/entity/relation.rs | 27 +++++++++++++++++++++++++ 10 files changed, 148 insertions(+) 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 From e4d115b5b0195b3654fcbbe41e8ce4de8f47ee96 Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Fri, 29 Oct 2021 10:39:37 +0300 Subject: [PATCH 09/18] Document the database drivers --- src/driver/mock.rs | 21 +++++++++++++++++++++ src/driver/sqlx_common.rs | 2 ++ src/driver/sqlx_mysql.rs | 11 +++++++++++ src/driver/sqlx_postgres.rs | 11 +++++++++++ src/driver/sqlx_sqlite.rs | 11 +++++++++++ 5 files changed, 56 insertions(+) diff --git a/src/driver/mock.rs b/src/driver/mock.rs index ad0c35cf..3f271759 100644 --- a/src/driver/mock.rs +++ b/src/driver/mock.rs @@ -12,32 +12,43 @@ use std::{ }, }; +/// Defines a database driver for the [MockDatabase] #[derive(Debug)] pub struct MockDatabaseConnector; +/// Defines a connection for the [MockDatabase] #[derive(Debug)] pub struct MockDatabaseConnection { counter: AtomicUsize, mocker: Mutex>, } +/// A set of constraints for any type wanting to perform operations on the [MockDatabase] pub trait MockDatabaseTrait: Send + Debug { + /// Execute a statement in the [MockDatabase] fn execute(&mut self, counter: usize, stmt: Statement) -> Result; + /// Execute a SQL query in the [MockDatabase] fn query(&mut self, counter: usize, stmt: Statement) -> Result, DbErr>; + /// Create a transaction that can be committed atomically fn begin(&mut self); + /// Commit a successful transaction atomically into the [MockDatabase] fn commit(&mut self); + /// Roll back a transaction since errors were encountered fn rollback(&mut self); + /// Get all logs from a [MockDatabase] and return a [Transaction] fn drain_transaction_log(&mut self) -> Vec; + /// Get the backend being used in the [MockDatabase] fn get_database_backend(&self) -> DbBackend; } impl MockDatabaseConnector { + /// Check if the database URI given and the [DatabaseBackend](crate::DatabaseBackend) selected are the same #[allow(unused_variables)] pub fn accepts(string: &str) -> bool { #[cfg(feature = "sqlx-mysql")] @@ -55,6 +66,7 @@ impl MockDatabaseConnector { false } + /// Cpnnect to the [MockDatabase] #[allow(unused_variables)] pub async fn connect(string: &str) -> Result { macro_rules! connect_mock_db { @@ -82,6 +94,7 @@ impl MockDatabaseConnector { } impl MockDatabaseConnection { + /// Create a connection to the [MockDatabase] pub fn new(m: M) -> Self where M: MockDatabaseTrait, @@ -96,16 +109,19 @@ impl MockDatabaseConnection { &self.mocker } + /// Get the [DatabaseBackend](crate::DatabaseBackend) being used by the [MockDatabase] pub fn get_database_backend(&self) -> DbBackend { self.mocker.lock().unwrap().get_database_backend() } + /// Execute the SQL statement in the [MockDatabase] pub fn execute(&self, statement: Statement) -> Result { debug_print!("{}", statement); let counter = self.counter.fetch_add(1, Ordering::SeqCst); self.mocker.lock().unwrap().execute(counter, statement) } + /// Return one [QueryResult] if the query was successful pub fn query_one(&self, statement: Statement) -> Result, DbErr> { debug_print!("{}", statement); let counter = self.counter.fetch_add(1, Ordering::SeqCst); @@ -113,12 +129,14 @@ impl MockDatabaseConnection { Ok(result.into_iter().next()) } + /// Return all [QueryResult]s if the query was successful pub fn query_all(&self, statement: Statement) -> Result, DbErr> { debug_print!("{}", statement); let counter = self.counter.fetch_add(1, Ordering::SeqCst); self.mocker.lock().unwrap().query(counter, statement) } + /// Return [QueryResult]s from a multi-query operation pub fn fetch( &self, statement: &Statement, @@ -129,14 +147,17 @@ impl MockDatabaseConnection { } } + /// Create a statement block of SQL statements that execute together. pub fn begin(&self) { self.mocker.lock().unwrap().begin() } + /// Commit a transaction atomically to the database pub fn commit(&self) { self.mocker.lock().unwrap().commit() } + /// Roll back a faulty transaction pub fn rollback(&self) { self.mocker.lock().unwrap().rollback() } diff --git a/src/driver/sqlx_common.rs b/src/driver/sqlx_common.rs index 5b1f7b35..5e035c9c 100644 --- a/src/driver/sqlx_common.rs +++ b/src/driver/sqlx_common.rs @@ -1,9 +1,11 @@ use crate::DbErr; +/// Converts an [sqlx::error] execution error to a [DbErr] pub fn sqlx_error_to_exec_err(err: sqlx::Error) -> DbErr { DbErr::Exec(err.to_string()) } +/// Converts an [sqlx::error] query error to a [DbErr] pub fn sqlx_error_to_query_err(err: sqlx::Error) -> DbErr { DbErr::Query(err.to_string()) } diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index 895707d1..a861b8df 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -15,19 +15,23 @@ use crate::{ use super::sqlx_common::*; +/// Defines the [sqlx::mysql] connector #[derive(Debug)] pub struct SqlxMySqlConnector; +/// Defines a sqlx MySQL pool #[derive(Debug, Clone)] pub struct SqlxMySqlPoolConnection { pool: MySqlPool, } impl SqlxMySqlConnector { + /// Check if the URI provided corresponds to `mysql://` for a MySQL database pub fn accepts(string: &str) -> bool { string.starts_with("mysql://") && string.parse::().is_ok() } + /// Add configuration options for the MySQL database pub async fn connect(options: ConnectOptions) -> Result { let mut opt = options .url @@ -48,12 +52,14 @@ impl SqlxMySqlConnector { } impl SqlxMySqlConnector { + /// Instantiate a sqlx pool connection to a [DatabaseConnection] pub fn from_sqlx_mysql_pool(pool: MySqlPool) -> DatabaseConnection { DatabaseConnection::SqlxMySqlPoolConnection(SqlxMySqlPoolConnection { pool }) } } impl SqlxMySqlPoolConnection { + /// Execute a [Statement] on a MySQL backend pub async fn execute(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); @@ -70,6 +76,7 @@ impl SqlxMySqlPoolConnection { } } + /// Get one result from a SQL query. Returns [Option::None] if no match was found pub async fn query_one(&self, stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); @@ -89,6 +96,7 @@ impl SqlxMySqlPoolConnection { } } + /// Get the results of a query returning them as a Vec<[QueryResult]> pub async fn query_all(&self, stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); @@ -105,6 +113,7 @@ impl SqlxMySqlPoolConnection { } } + /// Stream the results of executing a SQL query pub async fn stream(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); @@ -117,6 +126,7 @@ impl SqlxMySqlPoolConnection { } } + /// Bundle a set of SQL statements that execute together. pub async fn begin(&self) -> Result { if let Ok(conn) = self.pool.acquire().await { DatabaseTransaction::new_mysql(conn).await @@ -127,6 +137,7 @@ impl SqlxMySqlPoolConnection { } } + /// Create a MySQL transaction pub async fn transaction(&self, callback: F) -> Result> where F: for<'b> FnOnce( diff --git a/src/driver/sqlx_postgres.rs b/src/driver/sqlx_postgres.rs index a9f138a4..84645a58 100644 --- a/src/driver/sqlx_postgres.rs +++ b/src/driver/sqlx_postgres.rs @@ -15,19 +15,23 @@ use crate::{ use super::sqlx_common::*; +/// Defines the [sqlx::postgres] connector #[derive(Debug)] pub struct SqlxPostgresConnector; +/// Defines a sqlx PostgreSQL pool #[derive(Debug, Clone)] pub struct SqlxPostgresPoolConnection { pool: PgPool, } impl SqlxPostgresConnector { + /// Check if the URI provided corresponds to `postgres://` for a PostgreSQL database pub fn accepts(string: &str) -> bool { string.starts_with("postgres://") && string.parse::().is_ok() } + /// Add configuration options for the MySQL database pub async fn connect(options: ConnectOptions) -> Result { let mut opt = options .url @@ -48,12 +52,14 @@ impl SqlxPostgresConnector { } impl SqlxPostgresConnector { + /// Instantiate a sqlx pool connection to a [DatabaseConnection] pub fn from_sqlx_postgres_pool(pool: PgPool) -> DatabaseConnection { DatabaseConnection::SqlxPostgresPoolConnection(SqlxPostgresPoolConnection { pool }) } } impl SqlxPostgresPoolConnection { + /// Execute a [Statement] on a PostgreSQL backend pub async fn execute(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); @@ -70,6 +76,7 @@ impl SqlxPostgresPoolConnection { } } + /// Get one result from a SQL query. Returns [Option::None] if no match was found pub async fn query_one(&self, stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); @@ -89,6 +96,7 @@ impl SqlxPostgresPoolConnection { } } + /// Get the results of a query returning them as a Vec<[QueryResult]> pub async fn query_all(&self, stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); @@ -105,6 +113,7 @@ impl SqlxPostgresPoolConnection { } } + /// Stream the results of executing a SQL query pub async fn stream(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); @@ -117,6 +126,7 @@ impl SqlxPostgresPoolConnection { } } + /// Bundle a set of SQL statements that execute together. pub async fn begin(&self) -> Result { if let Ok(conn) = self.pool.acquire().await { DatabaseTransaction::new_postgres(conn).await @@ -127,6 +137,7 @@ impl SqlxPostgresPoolConnection { } } + /// Create a PostgreSQL transaction pub async fn transaction(&self, callback: F) -> Result> where F: for<'b> FnOnce( diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 2001fac1..4e95bbaa 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -15,19 +15,23 @@ use crate::{ use super::sqlx_common::*; +/// Defines the [sqlx::sqlite] connector #[derive(Debug)] pub struct SqlxSqliteConnector; +/// Defines a sqlx SQLite pool #[derive(Debug, Clone)] pub struct SqlxSqlitePoolConnection { pool: SqlitePool, } impl SqlxSqliteConnector { + /// Check if the URI provided corresponds to `sqlite:` for a SQLite database pub fn accepts(string: &str) -> bool { string.starts_with("sqlite:") && string.parse::().is_ok() } + /// Add configuration options for the SQLite database pub async fn connect(options: ConnectOptions) -> Result { let mut opt = options .url @@ -53,12 +57,14 @@ impl SqlxSqliteConnector { } impl SqlxSqliteConnector { + /// Instantiate a sqlx pool connection to a [DatabaseConnection] pub fn from_sqlx_sqlite_pool(pool: SqlitePool) -> DatabaseConnection { DatabaseConnection::SqlxSqlitePoolConnection(SqlxSqlitePoolConnection { pool }) } } impl SqlxSqlitePoolConnection { + /// Execute a [Statement] on a SQLite backend pub async fn execute(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); @@ -75,6 +81,7 @@ impl SqlxSqlitePoolConnection { } } + /// Get one result from a SQL query. Returns [Option::None] if no match was found pub async fn query_one(&self, stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); @@ -94,6 +101,7 @@ impl SqlxSqlitePoolConnection { } } + /// Get the results of a query returning them as a Vec<[QueryResult]> pub async fn query_all(&self, stmt: Statement) -> Result, DbErr> { debug_print!("{}", stmt); @@ -110,6 +118,7 @@ impl SqlxSqlitePoolConnection { } } + /// Stream the results of executing a SQL query pub async fn stream(&self, stmt: Statement) -> Result { debug_print!("{}", stmt); @@ -122,6 +131,7 @@ impl SqlxSqlitePoolConnection { } } + /// Bundle a set of SQL statements that execute together. pub async fn begin(&self) -> Result { if let Ok(conn) = self.pool.acquire().await { DatabaseTransaction::new_sqlite(conn).await @@ -132,6 +142,7 @@ impl SqlxSqlitePoolConnection { } } + /// Create a MySQL transaction pub async fn transaction(&self, callback: F) -> Result> where F: for<'b> FnOnce( From f21492bf6067543b8714486fbbe9c1382477ffae Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Fri, 29 Oct 2021 10:40:16 +0300 Subject: [PATCH 10/18] Document the error module --- src/error.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/error.rs b/src/error.rs index f39aee72..f8412c05 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,15 @@ +/// An error from unsuccessful database operations #[derive(Debug, PartialEq)] pub enum DbErr { + /// There was a problem with the database connection Conn(String), + /// An operation did not execute successfully Exec(String), + /// An error occurred while performing a query Query(String), + /// The record was not found in the database RecordNotFound(String), + /// A custom error Custom(String), } @@ -21,6 +27,7 @@ impl std::fmt::Display for DbErr { } } +/// An error from a failed column operation when trying to convert the column to a string #[derive(Debug, Clone)] pub struct ColumnFromStrErr(pub String); From 037eab974e2fea9a608e597549dbefaefd1c58a7 Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Fri, 29 Oct 2021 10:40:39 +0300 Subject: [PATCH 11/18] Document the utils module --- src/util.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/util.rs b/src/util.rs index 20cc8eb3..9ffa1e4c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,3 +1,17 @@ +/// Uses the `log` crate to perform logging. +/// This must be enabled using the feature flag `debug-print`. +/// ### Usage +/// ``` +/// use sea_orm::debug_print; +/// +/// #[derive(Debug)] +/// enum FooError { +/// Bar, +/// Baz, +/// } +/// +/// debug_print!("{:?}", FooError::Bar); +/// ``` #[macro_export] #[cfg(feature = "debug-print")] macro_rules! debug_print { @@ -5,7 +19,7 @@ macro_rules! debug_print { } #[macro_export] -// Non-debug version +/// Non-debug version #[cfg(not(feature = "debug-print"))] macro_rules! debug_print { ($( $args:expr ),*) => { From e0023611a2eed72b1885a5f2a3b7e594f05c4482 Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Sat, 30 Oct 2021 11:15:43 +0300 Subject: [PATCH 12/18] Improve documentation for the drivers and the entity Provide module level code example on how to create an Entity, Model, ActiveModel, Column and PrimaryKey --- src/driver/sqlx_mysql.rs | 2 +- src/driver/sqlx_sqlite.rs | 4 +- src/entity/active_model.rs | 38 +++++++++++- src/entity/base_entity.rs | 1 + src/entity/mod.rs | 122 +++++++++++++++++++++++++++++++++++++ src/entity/primary_key.rs | 33 +++++++++- 6 files changed, 194 insertions(+), 6 deletions(-) diff --git a/src/driver/sqlx_mysql.rs b/src/driver/sqlx_mysql.rs index a861b8df..b2b89c68 100644 --- a/src/driver/sqlx_mysql.rs +++ b/src/driver/sqlx_mysql.rs @@ -126,7 +126,7 @@ impl SqlxMySqlPoolConnection { } } - /// Bundle a set of SQL statements that execute together. + /// Bundle a set of SQL statements that execute together. pub async fn begin(&self) -> Result { if let Ok(conn) = self.pool.acquire().await { DatabaseTransaction::new_mysql(conn).await diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index 4139a0f9..f9e10712 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -30,7 +30,7 @@ impl SqlxSqliteConnector { pub fn accepts(string: &str) -> bool { string.starts_with("sqlite:") && string.parse::().is_ok() } - + /// Add configuration options for the SQLite database pub async fn connect(options: ConnectOptions) -> Result { let mut opt = options @@ -129,7 +129,7 @@ impl SqlxSqlitePoolConnection { } } - /// Bundle a set of SQL statements that execute together. + /// Bundle a set of SQL statements that execute together. pub async fn begin(&self) -> Result { if let Ok(conn) = self.pool.acquire().await { DatabaseTransaction::new_sqlite(conn).await diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 7945d6dc..57e2569d 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -55,7 +55,27 @@ where ActiveValue::unchanged(value) } -/// Enforces a set of constraints on any type performing an Create, Update or Delete operation +/// Enforces a set of constraints on any type performing an Create, Update or Delete operation. +/// The type must also implement the [EntityTrait]. +/// #### Example +/// ``` +/// use sea_orm::entity::prelude::*; +/// +/// #[derive(Copy, Clone, Default, Debug, DeriveEntity)] +/// pub struct Entity; +/// +/// impl EntityName for Entity { +/// fn table_name(&self) -> &str { +/// "cake" +/// } +/// } +/// +/// #[derive(Clone, Debug, PartialEq, DeriveActiveModel)] +/// pub struct Model { +/// pub id: i32, +/// pub name: Option , +/// } +/// ``` #[async_trait] pub trait ActiveModelTrait: Clone + Debug { /// Enforce the type to the constraints of the [EntityTrait] @@ -186,7 +206,21 @@ pub trait ActiveModelTrait: Clone + Debug { } /// Enforce a set of constraints to a override the ActiveModel behavior -/// Behaviors for users to override +/// Behaviors for users to override. +/// The type must also implement the [ActiveModelTrait] +/// +/// ### Example +/// ``` +/// /// Derive the active +/// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +/// pub struct Model { +/// pub id: i32, +/// pub name: String, +/// pub cake_id: Option, +/// } +/// ``` +/// impl ActiveModelBehavior for ActiveModel {} +/// #[allow(unused_variables)] pub trait ActiveModelBehavior: ActiveModelTrait { /// Create a new ActiveModel with default values. Also used by `Default::default()`. diff --git a/src/entity/base_entity.rs b/src/entity/base_entity.rs index 4d3e1d87..0fe504dd 100644 --- a/src/entity/base_entity.rs +++ b/src/entity/base_entity.rs @@ -37,6 +37,7 @@ pub trait EntityName: IdenStatic + Default { } } +/// FIXME Add docs for manual impl /// An Entity implementing `EntityTrait` represents a table in a database. /// /// This trait provides an API for you to inspect it's properties diff --git a/src/entity/mod.rs b/src/entity/mod.rs index f78795dc..8b4fe6f8 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -1,3 +1,125 @@ +//! This modules contains types and traits for an Entity, ActiveMode, Model, PrimaryKey, ForeignKey and Relations. +//! +//! // An Entity +//! A unit struct implements [EntityTrait](crate::EntityTrait) representing a table in the database. +//! +//! This trait contains the properties of an entity including +//! +//! Table Name (implemented EntityName) +//! Column (implemented ColumnTrait) +//! Relation (implemented RelationTrait) +//! Primary Key (implemented PrimaryKeyTrait and PrimaryKeyToColumn) +//! +//! This trait also provides an API for CRUD actions +//! +//! Select: find, find_* +//! Insert: insert, insert_* +//! Update: update, update_* +//! Delete: delete, delete_* +//! +//! #### Example for creating an Entity, Model and ActiveModel +//! ``` +//! use sea_orm::entity::prelude::*; +//! +//! // Use [DeriveEntity] to derive the EntityTrait automatically +//! #[derive(Copy, Clone, Default, Debug, DeriveEntity)] +//! pub struct Entity; +//! +//! /// The [EntityName] describes the name of a table +//! impl EntityName for Entity { +//! fn table_name(&self) -> &str { +//! "cake" +//! } +//! } +//! +//! // Create a Model for the Entity through [DeriveModel]. +//! // The `Model` handles `READ` operations on a table in a database. +//! // The [DeriveActiveModel] creates a way to perform `CREATE` , `READ` and `UPDATE` operations +//! // in a database +//! +//! #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +//! pub struct Model { +//! pub id: i32, +//! pub name: Option , +//! } +//! +//! // Use the [DeriveColumn] to create a Column for an the table called Entity +//! // The [EnumIter] which creates a new type that iterates of the variants of a Column. +//! #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +//! pub enum Column { +//! Id, +//! Name, +//! } +//! +//! // Create a PrimaryKey for the Entity using the [PrimaryKeyTrait] +//! // The [EnumIter] which creates a new type that iterates of the variants of a PrimaryKey. +//! #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +//! pub enum PrimaryKey { +//! Id, +//! } +//! +//! // Or implement the [PrimaryKeyTrait] manually instead of using the macro [DerivePrimaryKey] +//! impl PrimaryKeyTrait for PrimaryKey { +//! type ValueType = i32; +//! +//! fn auto_increment() -> bool { +//! true +//! } +//! } +//! +//! // Create a Relation for the Entity +//! #[derive(Copy, Clone, Debug, EnumIter)] +//! #[sea_orm( +//! // The relation belongs to `Entity` type +//! belongs_to = "Entity", +//! from = "Column::FruitId", +//! to = "Column::Id" +//! )] +//! pub enum Relation { +//! Fruit, +//! } +//! +//! // Create the properties of a Column in an Entity ensuring that calling +//! // Column::def() yields a Column definition as defined in [ColumnDef] +//! impl ColumnTrait for Column { +//! type EntityName = Entity; +//! fn def(&self) -> ColumnDef { +//! match self { +//! Self::Id => ColumnType::Integer.def(), +//! Self::Name => ColumnType::Text.def().null(), +//! } +//! } +//! } +//! +//! // Implement the set of constraints for creating a Relation as defined in the [RelationTrait] +//! impl RelationTrait for Relation { +//! fn def(&self) -> RelationDef { +//! match self { +//! Self::Fruit => Entity::has_many(super::fruit::Entity).into(), +//! } +//! } +//! } +//! +//! impl Related for Entity { +//! fn to() -> RelationDef { +//! Relation::Fruit.def() +//! } +//! } +//! +//! impl Related for Entity { +//! fn to() -> RelationDef { +//! super::cake_filling::Relation::Filling.def() +//! } +//! fn via() -> Option { +//! Some(super::cake_filling::Relation::Cake.def().rev()) +//! } +//! } +//! +//! // Implement user defined operations for CREATE, UPDATE and DELETE operations +//! // to create an ActiveModel using the [ActiveModelBehavior] +//! impl ActiveModelBehavior for ActiveModel {} +//! ``` + mod active_model; mod base_entity; mod column; diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index 17015fa4..39969888 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -4,7 +4,38 @@ 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 +/// A set of constraints to be used to define a Primary Key. +/// +/// A primary key can be derived manually +/// +/// ### Example +/// ``` +/// use sea_orm::entity::prelude::*; +/// +/// #[derive(Copy, Clone, Debug, EnumIter)] +/// pub enum PrimaryKey { +/// Id, +/// } +/// impl PrimaryKeyTrait for PrimaryKey { +/// type ValueType = i32; +/// +/// fn auto_increment() -> bool { +/// true +/// } +/// } +/// ``` +/// +/// Alternatively, use derive macros to automatically implement the trait for a Primary Key +/// +/// ### Example +/// ``` +/// use sea_orm::entity::prelude::*; +/// +/// #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +/// pub enum PrimaryKey { +/// Id, +/// } +/// ``` pub trait PrimaryKeyTrait: IdenStatic + Iterable { #[allow(missing_docs)] type ValueType: Sized From 870ca3ffb0a783eaa2c46aadbdcb42e6963e2064 Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Sat, 30 Oct 2021 12:00:07 +0300 Subject: [PATCH 13/18] Run fmt --all --- sea-orm-macros/src/lib.rs | 22 +++++++++++----------- src/entity/active_model.rs | 5 +++-- src/entity/base_entity.rs | 3 +-- src/entity/mod.rs | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index 87ac07d0..b770f1c8 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -12,8 +12,8 @@ mod util; /// ``` /// use sea_orm::entity::prelude::*; /// -///#[derive(Copy, Clone, Default, Debug, DeriveEntity)] -///pub struct Entity; +/// #[derive(Copy, Clone, Default, Debug, DeriveEntity)] +/// pub struct Entity; /// ``` #[proc_macro_derive(DeriveEntity, attributes(sea_orm))] pub fn derive_entity(input: TokenStream) -> TokenStream { @@ -150,13 +150,11 @@ pub fn derive_model(input: TokenStream) -> TokenStream { /// ### Usage /// /// ``` -/// /// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] /// pub struct Model { /// pub id: i32, /// pub name: String, /// } -/// /// ``` #[proc_macro_derive(DeriveActiveModel, attributes(sea_orm))] pub fn derive_active_model(input: TokenStream) -> TokenStream { @@ -183,7 +181,9 @@ pub fn derive_into_active_model(input: TokenStream) -> TokenStream { /// ``` /// use sea_orm::entity::prelude::*; /// -/// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, DeriveActiveModelBehavior,)] +/// #[derive( +/// Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, DeriveActiveModelBehavior, +/// )] /// pub struct Model { /// pub id: i32, /// pub name: String, @@ -203,11 +203,11 @@ pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream { /// ### Usage /// /// ``` -/// #[derive(Debug, FromQueryResult)] -/// struct SelectResult { -/// name: String, -/// num_of_fruits: i32, -/// } +/// #[derive(Debug, FromQueryResult)] +/// struct SelectResult { +/// name: String, +/// num_of_fruits: i32, +/// } /// ``` #[proc_macro_derive(FromQueryResult)] pub fn derive_from_query_result(input: TokenStream) -> TokenStream { @@ -224,7 +224,7 @@ pub fn derive_from_query_result(input: TokenStream) -> TokenStream { /// ``` /// #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] /// pub enum Relation { -/// #[sea_orm( +/// #[sea_orm( /// belongs_to = "super::cake::Entity", /// from = "Column::CakeId", /// to = "super::cake::Column::Id" diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index 57e2569d..af1fc339 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -73,7 +73,7 @@ where /// #[derive(Clone, Debug, PartialEq, DeriveActiveModel)] /// pub struct Model { /// pub id: i32, -/// pub name: Option , +/// pub name: Option, /// } /// ``` #[async_trait] @@ -211,6 +211,8 @@ pub trait ActiveModelTrait: Clone + Debug { /// /// ### Example /// ``` +/// use sea_orm::entity::prelude::*; +/// /// /// Derive the active /// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] /// pub struct Model { @@ -220,7 +222,6 @@ pub trait ActiveModelTrait: Clone + Debug { /// } /// ``` /// impl ActiveModelBehavior for ActiveModel {} -/// #[allow(unused_variables)] pub trait ActiveModelBehavior: ActiveModelTrait { /// Create a new ActiveModel with default values. Also used by `Default::default()`. diff --git a/src/entity/base_entity.rs b/src/entity/base_entity.rs index 0fe504dd..c03199d7 100644 --- a/src/entity/base_entity.rs +++ b/src/entity/base_entity.rs @@ -37,7 +37,6 @@ pub trait EntityName: IdenStatic + Default { } } -/// FIXME Add docs for manual impl /// An Entity implementing `EntityTrait` represents a table in a database. /// /// This trait provides an API for you to inspect it's properties @@ -79,7 +78,7 @@ pub trait EntityTrait: EntityName { RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true) } - /// Chech if the Entity has many relations + /// Check if the Entity has many relations fn has_many(_: R) -> RelationBuilder where R: EntityTrait + Related, diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 8b4fe6f8..08280326 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -40,7 +40,7 @@ //! #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] //! pub struct Model { //! pub id: i32, -//! pub name: Option , +//! pub name: Option, //! } //! //! // Use the [DeriveColumn] to create a Column for an the table called Entity From a1167b774e16139b4171e3b975a702ab5c62e88c Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sat, 30 Oct 2021 22:12:55 +0800 Subject: [PATCH 14/18] Try `cargo test` with different args --- .github/workflows/rust.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 521238af..a2704e88 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -260,6 +260,27 @@ jobs: args: > --manifest-path sea-orm-cli/Cargo.toml + test-temp: + name: Unit Test (Temp) + runs-on: ubuntu-20.04 + strategy: + matrix: + args: ["--workspace", "--doc", "--all-targets"] + fail-fast: false + steps: + - uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + + - uses: actions-rs/cargo@v1 + with: + command: test + args: ${{ matrix.args }} + cli: name: CLI needs: init From a22350914c0318a48648fa3b6cfe4d36a1a3ae7f Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Sat, 30 Oct 2021 18:55:53 +0300 Subject: [PATCH 15/18] Solutions for doc tests --- src/entity/active_model.rs | 40 +++---- src/entity/mod.rs | 223 +++++++++++++++++-------------------- src/entity/primary_key.rs | 5 +- 3 files changed, 121 insertions(+), 147 deletions(-) diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index af1fc339..abf96014 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -57,25 +57,7 @@ where /// Enforces a set of constraints on any type performing an Create, Update or Delete operation. /// The type must also implement the [EntityTrait]. -/// #### Example -/// ``` -/// use sea_orm::entity::prelude::*; -/// -/// #[derive(Copy, Clone, Default, Debug, DeriveEntity)] -/// pub struct Entity; -/// -/// impl EntityName for Entity { -/// fn table_name(&self) -> &str { -/// "cake" -/// } -/// } -/// -/// #[derive(Clone, Debug, PartialEq, DeriveActiveModel)] -/// pub struct Model { -/// pub id: i32, -/// pub name: Option, -/// } -/// ``` +/// See module level docs [crate::entity] for a full example #[async_trait] pub trait ActiveModelTrait: Clone + Debug { /// Enforce the type to the constraints of the [EntityTrait] @@ -210,18 +192,30 @@ pub trait ActiveModelTrait: Clone + Debug { /// The type must also implement the [ActiveModelTrait] /// /// ### Example -/// ``` +/// ```ignore /// use sea_orm::entity::prelude::*; /// -/// /// Derive the active +/// // Use [DeriveEntity] to derive the EntityTrait automatically +/// #[derive(Copy, Clone, Default, Debug, DeriveEntity)] +/// pub struct Entity; +/// +/// /// The [EntityName] describes the name of a table +/// impl EntityName for Entity { +/// fn table_name(&self) -> &str { +/// "cake" +/// } +/// } +/// +/// // Derive the ActiveModel /// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] /// pub struct Model { /// pub id: i32, /// pub name: String, -/// pub cake_id: Option, /// } -/// ``` +/// /// impl ActiveModelBehavior for ActiveModel {} +/// ``` +/// See module level docs [crate::entity] for a full example #[allow(unused_variables)] pub trait ActiveModelBehavior: ActiveModelTrait { /// Create a new ActiveModel with default values. Also used by `Default::default()`. diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 08280326..76187def 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -1,125 +1,104 @@ -//! This modules contains types and traits for an Entity, ActiveMode, Model, PrimaryKey, ForeignKey and Relations. -//! -//! // An Entity -//! A unit struct implements [EntityTrait](crate::EntityTrait) representing a table in the database. -//! -//! This trait contains the properties of an entity including -//! -//! Table Name (implemented EntityName) -//! Column (implemented ColumnTrait) -//! Relation (implemented RelationTrait) -//! Primary Key (implemented PrimaryKeyTrait and PrimaryKeyToColumn) -//! -//! This trait also provides an API for CRUD actions -//! -//! Select: find, find_* -//! Insert: insert, insert_* -//! Update: update, update_* -//! Delete: delete, delete_* -//! -//! #### Example for creating an Entity, Model and ActiveModel -//! ``` -//! use sea_orm::entity::prelude::*; -//! -//! // Use [DeriveEntity] to derive the EntityTrait automatically -//! #[derive(Copy, Clone, Default, Debug, DeriveEntity)] -//! pub struct Entity; -//! -//! /// The [EntityName] describes the name of a table -//! impl EntityName for Entity { -//! fn table_name(&self) -> &str { -//! "cake" -//! } -//! } -//! -//! // Create a Model for the Entity through [DeriveModel]. -//! // The `Model` handles `READ` operations on a table in a database. -//! // The [DeriveActiveModel] creates a way to perform `CREATE` , `READ` and `UPDATE` operations -//! // in a database -//! -//! #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] -//! pub struct Model { -//! pub id: i32, -//! pub name: Option, -//! } -//! -//! // Use the [DeriveColumn] to create a Column for an the table called Entity -//! // The [EnumIter] which creates a new type that iterates of the variants of a Column. -//! #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] -//! pub enum Column { -//! Id, -//! Name, -//! } -//! -//! // Create a PrimaryKey for the Entity using the [PrimaryKeyTrait] -//! // The [EnumIter] which creates a new type that iterates of the variants of a PrimaryKey. -//! #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] -//! pub enum PrimaryKey { -//! Id, -//! } -//! -//! // Or implement the [PrimaryKeyTrait] manually instead of using the macro [DerivePrimaryKey] -//! impl PrimaryKeyTrait for PrimaryKey { -//! type ValueType = i32; -//! -//! fn auto_increment() -> bool { -//! true -//! } -//! } -//! -//! // Create a Relation for the Entity -//! #[derive(Copy, Clone, Debug, EnumIter)] -//! #[sea_orm( -//! // The relation belongs to `Entity` type -//! belongs_to = "Entity", -//! from = "Column::FruitId", -//! to = "Column::Id" -//! )] -//! pub enum Relation { -//! Fruit, -//! } -//! -//! // Create the properties of a Column in an Entity ensuring that calling -//! // Column::def() yields a Column definition as defined in [ColumnDef] -//! impl ColumnTrait for Column { -//! type EntityName = Entity; -//! fn def(&self) -> ColumnDef { -//! match self { -//! Self::Id => ColumnType::Integer.def(), -//! Self::Name => ColumnType::Text.def().null(), -//! } -//! } -//! } -//! -//! // Implement the set of constraints for creating a Relation as defined in the [RelationTrait] -//! impl RelationTrait for Relation { -//! fn def(&self) -> RelationDef { -//! match self { -//! Self::Fruit => Entity::has_many(super::fruit::Entity).into(), -//! } -//! } -//! } -//! -//! impl Related for Entity { -//! fn to() -> RelationDef { -//! Relation::Fruit.def() -//! } -//! } -//! -//! impl Related for Entity { -//! fn to() -> RelationDef { -//! super::cake_filling::Relation::Filling.def() -//! } -//! fn via() -> Option { -//! Some(super::cake_filling::Relation::Cake.def().rev()) -//! } -//! } -//! -//! // Implement user defined operations for CREATE, UPDATE and DELETE operations -//! // to create an ActiveModel using the [ActiveModelBehavior] -//! impl ActiveModelBehavior for ActiveModel {} -//! ``` - +/// This modules contains types and traits for an Entity, ActiveMode, Model, PrimaryKey, ForeignKey and Relations. +/// +/// // An Entity +/// A unit struct implements [EntityTrait](crate::EntityTrait) representing a table in the database. +/// +/// This trait contains the properties of an entity including +/// +/// - The Table Name which is implemented by [EntityName](crate::EntityName) +/// - The Column which is implemented by [ColumnTrait](crate::ColumnTrait) +/// - A Relation which is implemented by [RelationTrait](crate::RelationTrait) +/// - The Primary Key which is implemented by [PrimaryKeyTrait](crate::PrimaryKeyTrait) +/// and [PrimaryKeyToColumn](crate::PrimaryKeyToColumn) +/// +/// This trait also provides an API for CRUD actions +/// +/// #### Example for creating an Entity, Model and ActiveModel +/// ``` +/// #[cfg(feature = "macros")] +/// +/// # use crate::entity::prelude::*; +/// # use sea_orm_macros::*; +/// use sea_orm::ActiveModelBehavior; +/// use sea_orm::RelationDef; +/// use sea_orm::RelationTrait; +/// use sea_orm::ColumnType; +/// use sea_orm::ColumnDef; +/// use sea_orm::ColumnTrait; +/// use sea_orm::PrimaryKeyTrait; +/// use sea_orm::EntityName; +/// +/// // Use [DeriveEntity] to derive the EntityTrait automatically +/// #[derive(Copy, Clone, Default, Debug, DeriveEntity)] +/// pub struct Entity; +/// +/// /// The [EntityName] describes the name of a table +/// impl EntityName for Entity { +/// fn table_name(&self) -> &str { +/// "filling" +/// } +/// } +/// +/// // Create a Model for the Entity through [DeriveModel]. +/// // The `Model` handles `READ` operations on a table in a database. +/// // The [DeriveActiveModel] creates a way to perform `CREATE` , `READ` and `UPDATE` operations +/// // in a database +/// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +/// pub struct Model { +/// pub id: i32, +/// pub name: String, +/// } +/// +/// // Use the [DeriveColumn] to create a Column for an the table called Entity +/// // The [EnumIter] which creates a new type that iterates of the variants of a Column. +/// #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +/// pub enum Column { +/// Id, +/// Name, +/// } +/// +/// // Create a PrimaryKey for the Entity using the [PrimaryKeyTrait] +/// // The [EnumIter] which creates a new type that iterates of the variants of a PrimaryKey. +/// #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +/// pub enum PrimaryKey { +/// Id, +/// } +/// +/// // Or implement the [PrimaryKeyTrait] manually instead of using the macro [DerivePrimaryKey] +/// impl PrimaryKeyTrait for PrimaryKey { +/// type ValueType = i32; +/// +/// fn auto_increment() -> bool { +/// true +/// } +/// } +/// +/// +/// #[derive(Copy, Clone, Debug, EnumIter)] +/// pub enum Relation {} +/// +/// impl ColumnTrait for Column { +/// type EntityName = Entity; +/// +/// fn def(&self) -> ColumnDef { +/// match self { +/// Self::Id => ColumnType::Integer.def(), +/// Self::Name => ColumnType::String(None).def(), +/// } +/// } +/// } +/// +/// // Create a Relation for the Entity +/// impl RelationTrait for Relation { +/// fn def(&self) -> RelationDef { +/// panic!() +/// } +/// } +/// // Implement user defined operations for CREATE, UPDATE and DELETE operations +/// // to create an ActiveModel using the [ActiveModelBehavior] +/// impl ActiveModelBehavior for ActiveModel {} +/// +/// ``` mod active_model; mod base_entity; mod column; diff --git a/src/entity/primary_key.rs b/src/entity/primary_key.rs index 39969888..d4075a2c 100644 --- a/src/entity/primary_key.rs +++ b/src/entity/primary_key.rs @@ -9,7 +9,7 @@ use std::fmt::Debug; /// A primary key can be derived manually /// /// ### Example -/// ``` +/// ```text /// use sea_orm::entity::prelude::*; /// /// #[derive(Copy, Clone, Debug, EnumIter)] @@ -28,7 +28,7 @@ use std::fmt::Debug; /// Alternatively, use derive macros to automatically implement the trait for a Primary Key /// /// ### Example -/// ``` +/// ```text /// use sea_orm::entity::prelude::*; /// /// #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] @@ -36,6 +36,7 @@ use std::fmt::Debug; /// Id, /// } /// ``` +/// See module level docs [crate::entity] for a full example pub trait PrimaryKeyTrait: IdenStatic + Iterable { #[allow(missing_docs)] type ValueType: Sized From 30814f0053bf0cfe6a8a0001cf63d31600926802 Mon Sep 17 00:00:00 2001 From: Charles Chege Date: Sun, 31 Oct 2021 09:03:02 +0300 Subject: [PATCH 16/18] =?UTF-8?q?Provide=20further=20exaplanation=20for=20?= =?UTF-8?q?p=1B[200pub=20struct=20ActiveValue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a code snippet for a simple example --- src/entity/active_model.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index abf96014..ed954b2d 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -5,7 +5,28 @@ use async_trait::async_trait; use sea_query::{Nullable, ValueTuple}; use std::fmt::Debug; -/// Defines a value from an ActiveModel and its state +/// Defines a value from an ActiveModel and its state. +/// The field `value` takes in an [Option] type where `Option::Some(V)` , with `V` holding +/// the value that operations like `UPDATE` are being performed on and +/// the `state` field is either `ActiveValueState::Set` or `ActiveValueState::Unchanged`. +/// [Option::None] in the `value` field indicates no value being performed by an operation +/// and that the `state` field of the [ActiveValue] is set to `ActiveValueState::Unset` . +/// #### Example snippet +/// ```no_run +/// // The code snipped below does an UPDATE operation on a [ActiveValue] +/// // yielding the the SQL statement ` r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"# ` +/// +/// use crate::tests_cfg::{cake, fruit}; +/// use crate::{entity::*, query::*, DbBackend}; +/// +/// Update::one(fruit::ActiveModel { +/// id: ActiveValue::set(1), +/// name: ActiveValue::set("Orange".to_owned()), +/// cake_id: ActiveValue::unset(), +/// }) +/// .build(DbBackend::Postgres) +/// .to_string(); +/// ``` #[derive(Clone, Debug, Default)] pub struct ActiveValue where From fcf3ea9407c7d20d8b8680b4c4fa2d16afe63121 Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 31 Oct 2021 14:40:36 +0800 Subject: [PATCH 17/18] Try fixing some doctest errors --- src/driver/sqlx_sqlite.rs | 1 + src/entity/active_model.rs | 4 ++-- src/entity/mod.rs | 3 +-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/driver/sqlx_sqlite.rs b/src/driver/sqlx_sqlite.rs index f9e10712..69eee575 100644 --- a/src/driver/sqlx_sqlite.rs +++ b/src/driver/sqlx_sqlite.rs @@ -33,6 +33,7 @@ impl SqlxSqliteConnector { /// Add configuration options for the SQLite database pub async fn connect(options: ConnectOptions) -> Result { + let mut options = options; let mut opt = options .url .parse::() diff --git a/src/entity/active_model.rs b/src/entity/active_model.rs index ed954b2d..d46c6dbf 100644 --- a/src/entity/active_model.rs +++ b/src/entity/active_model.rs @@ -16,8 +16,8 @@ use std::fmt::Debug; /// // The code snipped below does an UPDATE operation on a [ActiveValue] /// // yielding the the SQL statement ` r#"UPDATE "fruit" SET "name" = 'Orange' WHERE "fruit"."id" = 1"# ` /// -/// use crate::tests_cfg::{cake, fruit}; -/// use crate::{entity::*, query::*, DbBackend}; +/// use sea_orm::tests_cfg::{cake, fruit}; +/// use sea_orm::{entity::*, query::*, DbBackend}; /// /// Update::one(fruit::ActiveModel { /// id: ActiveValue::set(1), diff --git a/src/entity/mod.rs b/src/entity/mod.rs index 76187def..c2dd508d 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -17,8 +17,7 @@ /// ``` /// #[cfg(feature = "macros")] /// -/// # use crate::entity::prelude::*; -/// # use sea_orm_macros::*; +/// # use sea_orm::entity::prelude::*; /// use sea_orm::ActiveModelBehavior; /// use sea_orm::RelationDef; /// use sea_orm::RelationTrait; From 6904b9f057b6ed10b0d42cc7115759574173483e Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Sun, 31 Oct 2021 15:45:42 +0800 Subject: [PATCH 18/18] Try fixing `sea-orm-macros` doctest errors --- sea-orm-macros/Cargo.toml | 4 + sea-orm-macros/src/lib.rs | 319 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 314 insertions(+), 9 deletions(-) diff --git a/sea-orm-macros/Cargo.toml b/sea-orm-macros/Cargo.toml index 453c1441..c62a0633 100644 --- a/sea-orm-macros/Cargo.toml +++ b/sea-orm-macros/Cargo.toml @@ -21,3 +21,7 @@ syn = { version = "^1", default-features = false, features = [ "full", "derive", quote = "^1" heck = "^0.3" proc-macro2 = "^1" + +[dev-dependencies] +sea-orm = { path = "../", features = ["macros"] } +serde = { version = "^1.0", features = ["derive"] } diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index b770f1c8..1514895c 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -8,12 +8,67 @@ mod derives; mod util; /// Create an Entity +/// /// ### Usage +/// /// ``` /// use sea_orm::entity::prelude::*; /// /// #[derive(Copy, Clone, Default, Debug, DeriveEntity)] /// pub struct Entity; +/// +/// # impl EntityName for Entity { +/// # fn table_name(&self) -> &str { +/// # "cake" +/// # } +/// # } +/// # +/// # #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +/// # pub struct Model { +/// # pub id: i32, +/// # pub name: String, +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +/// # pub enum Column { +/// # Id, +/// # Name, +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +/// # pub enum PrimaryKey { +/// # Id, +/// # } +/// # +/// # impl PrimaryKeyTrait for PrimaryKey { +/// # type ValueType = i32; +/// # +/// # fn auto_increment() -> bool { +/// # true +/// # } +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter)] +/// # pub enum Relation {} +/// # +/// # impl ColumnTrait for Column { +/// # type EntityName = Entity; +/// # +/// # fn def(&self) -> ColumnDef { +/// # match self { +/// # Self::Id => ColumnType::Integer.def(), +/// # Self::Name => ColumnType::String(None).def(), +/// # } +/// # } +/// # } +/// # +/// # impl RelationTrait for Relation { +/// # fn def(&self) -> RelationDef { +/// # panic!("No Relation"); +/// # } +/// # } +/// # +/// # impl ActiveModelBehavior for ActiveModel {} /// ``` #[proc_macro_derive(DeriveEntity, attributes(sea_orm))] pub fn derive_entity(input: TokenStream) -> TokenStream { @@ -25,10 +80,14 @@ pub fn derive_entity(input: TokenStream) -> TokenStream { /// This derive macro is the 'almighty' macro which automatically generates /// Entity, Column, and PrimaryKey from a given Model. -/// ### Usage -/// use sea_orm::entity::prelude::*; /// -/// #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize, FromForm)] +/// ### Usage +/// +/// ``` +/// use sea_orm::entity::prelude::*; +/// use serde::{Deserialize, Serialize}; +/// +/// #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)] /// #[sea_orm(table_name = "posts")] /// pub struct Model { /// #[sea_orm(primary_key)] @@ -37,6 +96,17 @@ pub fn derive_entity(input: TokenStream) -> TokenStream { /// #[sea_orm(column_type = "Text")] /// pub text: String, /// } +/// +/// # #[derive(Copy, Clone, Debug, EnumIter)] +/// # pub enum Relation {} +/// # +/// # impl RelationTrait for Relation { +/// # fn def(&self) -> RelationDef { +/// # panic!("No Relation"); +/// # } +/// # } +/// # +/// # impl ActiveModelBehavior for ActiveModel {} /// ``` #[proc_macro_derive(DeriveEntityModel, attributes(sea_orm))] pub fn derive_entity_model(input: TokenStream) -> TokenStream { @@ -62,7 +132,9 @@ pub fn derive_entity_model(input: TokenStream) -> TokenStream { /// The DerivePrimaryKey derive macro will implement [PrimaryKeyToColumn] /// for PrimaryKey which defines tedious mappings between primary keys and columns. /// The [EnumIter] is also derived, allowing iteration over all enum variants. +/// /// ### Usage +/// /// ``` /// use sea_orm::entity::prelude::*; /// @@ -71,6 +143,57 @@ pub fn derive_entity_model(input: TokenStream) -> TokenStream { /// CakeId, /// FillingId, /// } +/// +/// # #[derive(Copy, Clone, Default, Debug, DeriveEntity)] +/// # pub struct Entity; +/// # +/// # impl EntityName for Entity { +/// # fn table_name(&self) -> &str { +/// # "cake" +/// # } +/// # } +/// # +/// # #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] +/// # pub struct Model { +/// # pub cake_id: i32, +/// # pub filling_id: i32, +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +/// # pub enum Column { +/// # CakeId, +/// # FillingId, +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter)] +/// # pub enum Relation {} +/// # +/// # impl ColumnTrait for Column { +/// # type EntityName = Entity; +/// # +/// # fn def(&self) -> ColumnDef { +/// # match self { +/// # Self::CakeId => ColumnType::Integer.def(), +/// # Self::FillingId => ColumnType::Integer.def(), +/// # } +/// # } +/// # } +/// # +/// # impl PrimaryKeyTrait for PrimaryKey { +/// # type ValueType = (i32, i32); +/// # +/// # fn auto_increment() -> bool { +/// # false +/// # } +/// # } +/// # +/// # impl RelationTrait for Relation { +/// # fn def(&self) -> RelationDef { +/// # panic!("No Relation"); +/// # } +/// # } +/// # +/// # impl ActiveModelBehavior for ActiveModel {} /// ``` #[proc_macro_derive(DerivePrimaryKey)] pub fn derive_primary_key(input: TokenStream) -> TokenStream { @@ -85,7 +208,9 @@ pub fn derive_primary_key(input: TokenStream) -> TokenStream { /// The DeriveColumn derive macro will implement [ColumnTrait] for Columns. /// It defines the identifier of each column by implementing Iden and IdenStatic. /// The EnumIter is also derived, allowing iteration over all enum variants. +/// /// ### Usage +/// /// ``` /// use sea_orm::entity::prelude::*; /// @@ -106,14 +231,27 @@ pub fn derive_column(input: TokenStream) -> TokenStream { } /// Derive a column if column names are not in snake-case +/// /// ### Usage +/// /// ``` +/// use sea_orm::entity::prelude::*; +/// /// #[derive(Copy, Clone, Debug, EnumIter, DeriveCustomColumn)] /// pub enum Column { /// Id, /// Name, /// VendorId, /// } +/// +/// impl IdenStatic for Column { +/// fn as_str(&self) -> &str { +/// match self { +/// Self::Id => "id", +/// _ => self.default_as_str(), +/// } +/// } +/// } /// ``` #[proc_macro_derive(DeriveCustomColumn)] pub fn derive_custom_column(input: TokenStream) -> TokenStream { @@ -128,14 +266,67 @@ pub fn derive_custom_column(input: TokenStream) -> TokenStream { /// The DeriveModel derive macro will implement ModelTrait for Model, /// which provides setters and getters for all attributes in the mod /// It also implements FromQueryResult to convert a query result into the corresponding Model. +/// /// ### Usage /// /// ``` +/// use sea_orm::entity::prelude::*; +/// /// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] /// pub struct Model { /// pub id: i32, /// pub name: String, /// } +/// +/// # #[derive(Copy, Clone, Default, Debug, DeriveEntity)] +/// # pub struct Entity; +/// # +/// # impl EntityName for Entity { +/// # fn table_name(&self) -> &str { +/// # "cake" +/// # } +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +/// # pub enum Column { +/// # Id, +/// # Name, +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +/// # pub enum PrimaryKey { +/// # Id, +/// # } +/// # +/// # impl PrimaryKeyTrait for PrimaryKey { +/// # type ValueType = i32; +/// # +/// # fn auto_increment() -> bool { +/// # true +/// # } +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter)] +/// # pub enum Relation {} +/// # +/// # impl ColumnTrait for Column { +/// # type EntityName = Entity; +/// # +/// # fn def(&self) -> ColumnDef { +/// # match self { +/// # Self::Id => ColumnType::Integer.def(), +/// # Self::Name => ColumnType::String(None).def(), +/// # } +/// # } +/// # } +/// # +/// # impl RelationTrait for Relation { +/// # fn def(&self) -> RelationDef { +/// # panic!("No Relation"); +/// # } +/// # } +/// # +/// # impl ActiveModelBehavior for ActiveModel {} /// ``` #[proc_macro_derive(DeriveModel, attributes(sea_orm))] pub fn derive_model(input: TokenStream) -> TokenStream { @@ -147,14 +338,67 @@ pub fn derive_model(input: TokenStream) -> TokenStream { /// The DeriveActiveModel derive macro will implement ActiveModelTrait for ActiveModel /// which provides setters and getters for all active values in the active model. +/// /// ### Usage /// /// ``` +/// use sea_orm::entity::prelude::*; +/// /// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] /// pub struct Model { /// pub id: i32, /// pub name: String, /// } +/// +/// # #[derive(Copy, Clone, Default, Debug, DeriveEntity)] +/// # pub struct Entity; +/// # +/// # impl EntityName for Entity { +/// # fn table_name(&self) -> &str { +/// # "cake" +/// # } +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +/// # pub enum Column { +/// # Id, +/// # Name, +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +/// # pub enum PrimaryKey { +/// # Id, +/// # } +/// # +/// # impl PrimaryKeyTrait for PrimaryKey { +/// # type ValueType = i32; +/// # +/// # fn auto_increment() -> bool { +/// # true +/// # } +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter)] +/// # pub enum Relation {} +/// # +/// # impl ColumnTrait for Column { +/// # type EntityName = Entity; +/// # +/// # fn def(&self) -> ColumnDef { +/// # match self { +/// # Self::Id => ColumnType::Integer.def(), +/// # Self::Name => ColumnType::String(None).def(), +/// # } +/// # } +/// # } +/// # +/// # impl RelationTrait for Relation { +/// # fn def(&self) -> RelationDef { +/// # panic!("No Relation"); +/// # } +/// # } +/// # +/// # impl ActiveModelBehavior for ActiveModel {} /// ``` #[proc_macro_derive(DeriveActiveModel, attributes(sea_orm))] pub fn derive_active_model(input: TokenStream) -> TokenStream { @@ -176,6 +420,7 @@ pub fn derive_into_active_model(input: TokenStream) -> TokenStream { } /// Models that a user can override +/// /// ### Usage /// /// ``` @@ -188,6 +433,54 @@ pub fn derive_into_active_model(input: TokenStream) -> TokenStream { /// pub id: i32, /// pub name: String, /// } +/// +/// # #[derive(Copy, Clone, Default, Debug, DeriveEntity)] +/// # pub struct Entity; +/// # +/// # impl EntityName for Entity { +/// # fn table_name(&self) -> &str { +/// # "cake" +/// # } +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] +/// # pub enum Column { +/// # Id, +/// # Name, +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] +/// # pub enum PrimaryKey { +/// # Id, +/// # } +/// # +/// # impl PrimaryKeyTrait for PrimaryKey { +/// # type ValueType = i32; +/// # +/// # fn auto_increment() -> bool { +/// # true +/// # } +/// # } +/// # +/// # #[derive(Copy, Clone, Debug, EnumIter)] +/// # pub enum Relation {} +/// # +/// # impl ColumnTrait for Column { +/// # type EntityName = Entity; +/// # +/// # fn def(&self) -> ColumnDef { +/// # match self { +/// # Self::Id => ColumnType::Integer.def(), +/// # Self::Name => ColumnType::String(None).def(), +/// # } +/// # } +/// # } +/// # +/// # impl RelationTrait for Relation { +/// # fn def(&self) -> RelationDef { +/// # panic!("No Relation"); +/// # } +/// # } /// ``` #[proc_macro_derive(DeriveActiveModelBehavior)] pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream { @@ -200,9 +493,12 @@ pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream { } /// Convert a query result into the corresponding Model. +/// /// ### Usage /// /// ``` +/// use sea_orm::{entity::prelude::*, FromQueryResult}; +/// /// #[derive(Debug, FromQueryResult)] /// struct SelectResult { /// name: String, @@ -220,20 +516,25 @@ pub fn derive_from_query_result(input: TokenStream) -> TokenStream { } /// The DeriveRelation derive macro will implement RelationTrait for Relation. +/// /// ### Usage +/// /// ``` +/// # use sea_orm::tests_cfg::fruit::Entity; +/// use sea_orm::entity::prelude::*; +/// /// #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] /// pub enum Relation { /// #[sea_orm( -/// belongs_to = "super::cake::Entity", -/// from = "Column::CakeId", -/// to = "super::cake::Column::Id" +/// belongs_to = "sea_orm::tests_cfg::cake::Entity", +/// from = "sea_orm::tests_cfg::fruit::Column::CakeId", +/// to = "sea_orm::tests_cfg::cake::Column::Id" /// )] /// Cake, /// #[sea_orm( -/// belongs_to = "super::cake_expanded::Entity", -/// from = "Column::CakeId", -/// to = "super::cake_expanded::Column::Id" +/// belongs_to = "sea_orm::tests_cfg::cake_expanded::Entity", +/// from = "sea_orm::tests_cfg::fruit::Column::CakeId", +/// to = "sea_orm::tests_cfg::cake_expanded::Column::Id" /// )] /// CakeExpanded, /// }