Merge remote-tracking branch 'origin/master' into active-enum

This commit is contained in:
Billy Chan 2021-11-02 12:18:37 +08:00
commit 6018d3f1b9
No known key found for this signature in database
GPG Key ID: A2D690CAC7DF3CC7
54 changed files with 1080 additions and 22 deletions

View File

@ -21,3 +21,7 @@ syn = { version = "^1", default-features = false, features = [ "full", "derive",
quote = "^1" quote = "^1"
heck = "^0.3" heck = "^0.3"
proc-macro2 = "^1" proc-macro2 = "^1"
[dev-dependencies]
sea-orm = { path = "../", features = ["macros"] }
serde = { version = "^1.0", features = ["derive"] }

View File

@ -1,6 +1,7 @@
pub mod derive_attr { pub mod derive_attr {
use bae::FromAttributes; use bae::FromAttributes;
/// Attributes for Models and ActiveModels
#[derive(Default, FromAttributes)] #[derive(Default, FromAttributes)]
pub struct SeaOrm { pub struct SeaOrm {
pub column: Option<syn::Ident>, pub column: Option<syn::Ident>,
@ -16,6 +17,7 @@ pub mod derive_attr {
pub mod field_attr { pub mod field_attr {
use bae::FromAttributes; use bae::FromAttributes;
/// Operations for Models and ActiveModels
#[derive(Default, FromAttributes)] #[derive(Default, FromAttributes)]
pub struct SeaOrm { pub struct SeaOrm {
pub belongs_to: Option<syn::Lit>, pub belongs_to: Option<syn::Lit>,

View File

@ -4,6 +4,7 @@ use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, quote_spanned}; use quote::{format_ident, quote, quote_spanned};
use syn::{punctuated::Punctuated, token::Comma, Data, DataStruct, Field, Fields, Lit, Meta, Type}; 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<TokenStream> { pub fn expand_derive_active_model(ident: Ident, data: Data) -> syn::Result<TokenStream> {
let fields = match data { let fields = match data {
Data::Struct(DataStruct { Data::Struct(DataStruct {

View File

@ -2,6 +2,7 @@ use proc_macro2::{Ident, TokenStream};
use quote::quote; use quote::quote;
use syn::Data; 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<TokenStream> { pub fn expand_derive_active_model_behavior(_ident: Ident, _data: Data) -> syn::Result<TokenStream> {
Ok(quote!( Ok(quote!(
#[automatically_derived] #[automatically_derived]

View File

@ -3,6 +3,7 @@ use proc_macro2::{Ident, TokenStream};
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned};
use syn::{punctuated::Punctuated, token::Comma, Data, DataEnum, Fields, Lit, Meta, Variant}; use syn::{punctuated::Punctuated, token::Comma, Data, DataEnum, Fields, Lit, Meta, Variant};
/// Derive a Column name for an enum type
pub fn impl_default_as_str(ident: &Ident, data: &Data) -> syn::Result<TokenStream> { pub fn impl_default_as_str(ident: &Ident, data: &Data) -> syn::Result<TokenStream> {
let variants = match data { let variants = match data {
syn::Data::Enum(DataEnum { variants, .. }) => variants, syn::Data::Enum(DataEnum { variants, .. }) => variants,
@ -65,6 +66,7 @@ pub fn impl_default_as_str(ident: &Ident, data: &Data) -> syn::Result<TokenStrea
)) ))
} }
/// Implement a column using for an enum using [DeriveColumn](sea_orm::DeriveColumn)
pub fn impl_col_from_str(ident: &Ident, data: &Data) -> syn::Result<TokenStream> { pub fn impl_col_from_str(ident: &Ident, data: &Data) -> syn::Result<TokenStream> {
let data_enum = match data { let data_enum = match data {
Data::Enum(data_enum) => data_enum, Data::Enum(data_enum) => data_enum,
@ -114,6 +116,7 @@ pub fn expand_derive_column(ident: &Ident, data: &Data) -> syn::Result<TokenStre
)) ))
} }
/// Derive a column with a non_snake_case name
pub fn expand_derive_custom_column(ident: &Ident, data: &Data) -> syn::Result<TokenStream> { pub fn expand_derive_custom_column(ident: &Ident, data: &Data) -> syn::Result<TokenStream> {
let impl_default_as_str = impl_default_as_str(ident, data)?; let impl_default_as_str = impl_default_as_str(ident, data)?;
let impl_col_from_str = impl_col_from_str(ident, data)?; let impl_col_from_str = impl_col_from_str(ident, data)?;

View File

@ -7,6 +7,7 @@ use syn::{
Lit, Meta, Lit, Meta,
}; };
/// Method to derive an Model
pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Result<TokenStream> { pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Result<TokenStream> {
// if #[sea_orm(table_name = "foo", schema_name = "bar")] specified, create Entity struct // if #[sea_orm(table_name = "foo", schema_name = "bar")] specified, create Entity struct
let mut table_name = None; let mut table_name = None;

View File

@ -2,6 +2,7 @@ use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, quote_spanned}; use quote::{format_ident, quote, quote_spanned};
use syn::{Data, DataStruct, Field, Fields}; 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<TokenStream> { pub fn expand_derive_from_query_result(ident: Ident, data: Data) -> syn::Result<TokenStream> {
let fields = match data { let fields = match data {
Data::Struct(DataStruct { Data::Struct(DataStruct {

View File

@ -2,6 +2,7 @@ use bae::FromAttributes;
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned};
/// Attributes to derive an ActiveModel
#[derive(Default, FromAttributes)] #[derive(Default, FromAttributes)]
pub struct SeaOrm { pub struct SeaOrm {
pub active_model: Option<syn::Ident>, pub active_model: Option<syn::Ident>,
@ -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<TokenStream> { pub fn expand_into_active_model(input: syn::DeriveInput) -> syn::Result<TokenStream> {
let ident_span = input.ident.span(); let ident_span = input.ident.span();

View File

@ -183,6 +183,7 @@ impl DeriveModel {
} }
} }
/// Method to derive an ActiveModel
pub fn expand_derive_model(input: syn::DeriveInput) -> syn::Result<TokenStream> { pub fn expand_derive_model(input: syn::DeriveInput) -> syn::Result<TokenStream> {
let ident_span = input.ident.span(); let ident_span = input.ident.span();

View File

@ -3,6 +3,7 @@ use proc_macro2::{Ident, TokenStream};
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned};
use syn::{Data, DataEnum, Fields, Variant}; 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<TokenStream> { pub fn expand_derive_primary_key(ident: Ident, data: Data) -> syn::Result<TokenStream> {
let variants = match data { let variants = match data {
syn::Data::Enum(DataEnum { variants, .. }) => variants, syn::Data::Enum(DataEnum { variants, .. }) => variants,

View File

@ -166,6 +166,7 @@ impl DeriveRelation {
} }
} }
/// Method to derive a Relation
pub fn expand_derive_relation(input: syn::DeriveInput) -> syn::Result<TokenStream> { pub fn expand_derive_relation(input: syn::DeriveInput) -> syn::Result<TokenStream> {
let ident_span = input.ident.span(); let ident_span = input.ident.span();

View File

@ -7,6 +7,69 @@ mod attributes;
mod derives; mod derives;
mod util; 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))] #[proc_macro_derive(DeriveEntity, attributes(sea_orm))]
pub fn derive_entity(input: TokenStream) -> TokenStream { pub fn derive_entity(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
@ -15,6 +78,36 @@ pub fn derive_entity(input: TokenStream) -> TokenStream {
.into() .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::*;
/// use serde::{Deserialize, Serialize};
///
/// #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)]
/// #[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,
/// }
///
/// # #[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))] #[proc_macro_derive(DeriveEntityModel, attributes(sea_orm))]
pub fn derive_entity_model(input: TokenStream) -> TokenStream { pub fn derive_entity_model(input: TokenStream) -> TokenStream {
let input_ts = input.clone(); let input_ts = input.clone();
@ -36,6 +129,72 @@ pub fn derive_entity_model(input: TokenStream) -> TokenStream {
ts 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,
/// }
///
/// # #[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)] #[proc_macro_derive(DerivePrimaryKey)]
pub fn derive_primary_key(input: TokenStream) -> TokenStream { pub fn derive_primary_key(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input); let DeriveInput { ident, data, .. } = parse_macro_input!(input);
@ -46,6 +205,21 @@ 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))] #[proc_macro_derive(DeriveColumn, attributes(sea_orm))]
pub fn derive_column(input: TokenStream) -> TokenStream { pub fn derive_column(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input); let DeriveInput { ident, data, .. } = parse_macro_input!(input);
@ -56,6 +230,29 @@ 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)] #[proc_macro_derive(DeriveCustomColumn)]
pub fn derive_custom_column(input: TokenStream) -> TokenStream { pub fn derive_custom_column(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input); let DeriveInput { ident, data, .. } = parse_macro_input!(input);
@ -66,6 +263,71 @@ 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))] #[proc_macro_derive(DeriveModel, attributes(sea_orm))]
pub fn derive_model(input: TokenStream) -> TokenStream { pub fn derive_model(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
@ -74,6 +336,70 @@ pub fn derive_model(input: TokenStream) -> TokenStream {
.into() .into()
} }
/// 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))] #[proc_macro_derive(DeriveActiveModel, attributes(sea_orm))]
pub fn derive_active_model(input: TokenStream) -> TokenStream { pub fn derive_active_model(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input); let DeriveInput { ident, data, .. } = parse_macro_input!(input);
@ -84,6 +410,7 @@ pub fn derive_active_model(input: TokenStream) -> TokenStream {
} }
} }
/// Derive into an active model
#[proc_macro_derive(DeriveIntoActiveModel, attributes(sea_orm))] #[proc_macro_derive(DeriveIntoActiveModel, attributes(sea_orm))]
pub fn derive_into_active_model(input: TokenStream) -> TokenStream { pub fn derive_into_active_model(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
@ -92,6 +419,69 @@ pub fn derive_into_active_model(input: TokenStream) -> TokenStream {
.into() .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,
/// }
///
/// # #[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)] #[proc_macro_derive(DeriveActiveModelBehavior)]
pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream { pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input); let DeriveInput { ident, data, .. } = parse_macro_input!(input);
@ -134,6 +524,19 @@ pub fn derive_active_enum(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,
/// num_of_fruits: i32,
/// }
/// ```
#[proc_macro_derive(FromQueryResult)] #[proc_macro_derive(FromQueryResult)]
pub fn derive_from_query_result(input: TokenStream) -> TokenStream { pub fn derive_from_query_result(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, .. } = parse_macro_input!(input); let DeriveInput { ident, data, .. } = parse_macro_input!(input);
@ -144,6 +547,30 @@ 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 = "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 = "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,
/// }
/// ```
#[proc_macro_derive(DeriveRelation, attributes(sea_orm))] #[proc_macro_derive(DeriveRelation, attributes(sea_orm))]
pub fn derive_relation(input: TokenStream) -> TokenStream { pub fn derive_relation(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);

View File

@ -4,23 +4,34 @@ use crate::{
use futures::Stream; use futures::Stream;
use std::{future::Future, pin::Pin}; 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] #[async_trait::async_trait]
pub trait ConnectionTrait<'a>: Sync { pub trait ConnectionTrait<'a>: Sync {
/// Create a stream for the [QueryResult]
type Stream: Stream<Item = Result<QueryResult, DbErr>>; type Stream: Stream<Item = Result<QueryResult, DbErr>>;
/// Fetch the database backend as specified in [DbBackend].
/// This depends on feature flags enabled.
fn get_database_backend(&self) -> DbBackend; fn get_database_backend(&self) -> DbBackend;
/// Execute a [Statement]
async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr>; async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr>;
/// Execute a [Statement] and return a query
async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr>; async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr>;
/// Execute a [Statement] and return a collection Vec<[QueryResult]> on success
async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr>; async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr>;
/// Execute a [Statement] and return a stream of results
fn stream( fn stream(
&'a self, &'a self,
stmt: Statement, stmt: Statement,
) -> Pin<Box<dyn Future<Output = Result<Self::Stream, DbErr>> + 'a>>; ) -> Pin<Box<dyn Future<Output = Result<Self::Stream, DbErr>> + 'a>>;
/// Execute SQL `BEGIN` transaction.
/// Returns a Transaction that can be committed or rolled back
async fn begin(&self) -> Result<DatabaseTransaction, DbErr>; async fn begin(&self) -> Result<DatabaseTransaction, DbErr>;
/// Execute the function inside a transaction. /// Execute the function inside a transaction.
@ -34,6 +45,7 @@ pub trait ConnectionTrait<'a>: Sync {
T: Send, T: Send,
E: std::error::Error + Send; E: std::error::Error + Send;
/// Check if the connection is a test connection for the Mock database
fn is_mock_connection(&self) -> bool { fn is_mock_connection(&self) -> bool {
false false
} }

View File

@ -12,30 +12,43 @@ use sqlx::pool::PoolConnection;
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
use std::sync::Arc; 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))] #[cfg_attr(not(feature = "mock"), derive(Clone))]
pub enum DatabaseConnection { pub enum DatabaseConnection {
/// Create a MYSQL database connection and pool
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection), SqlxMySqlPoolConnection(crate::SqlxMySqlPoolConnection),
/// Create a PostgreSQL database connection and pool
#[cfg(feature = "sqlx-postgres")] #[cfg(feature = "sqlx-postgres")]
SqlxPostgresPoolConnection(crate::SqlxPostgresPoolConnection), SqlxPostgresPoolConnection(crate::SqlxPostgresPoolConnection),
/// Create a SQLite database connection and pool
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
SqlxSqlitePoolConnection(crate::SqlxSqlitePoolConnection), SqlxSqlitePoolConnection(crate::SqlxSqlitePoolConnection),
/// Create a Mock database connection useful for testing
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
MockDatabaseConnection(Arc<crate::MockDatabaseConnection>), MockDatabaseConnection(Arc<crate::MockDatabaseConnection>),
/// The connection to the database has been severed
Disconnected, Disconnected,
} }
/// The same as a [DatabaseConnection]
pub type DbConn = 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)] #[derive(Debug, Copy, Clone, PartialEq)]
pub enum DatabaseBackend { pub enum DatabaseBackend {
/// A MySQL backend
MySql, MySql,
/// A PostgreSQL backend
Postgres, Postgres,
/// A SQLite backend
Sqlite, Sqlite,
} }
/// The same as [DatabaseBackend] just shorter :)
pub type DbBackend = DatabaseBackend; pub type DbBackend = DatabaseBackend;
pub(crate) enum InnerConnection { pub(crate) enum InnerConnection {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
MySql(PoolConnection<sqlx::MySql>), MySql(PoolConnection<sqlx::MySql>),
@ -209,6 +222,7 @@ impl<'a> ConnectionTrait<'a> for DatabaseConnection {
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
impl DatabaseConnection { impl DatabaseConnection {
/// Generate a database connection for testing the Mock database
pub fn as_mock_connection(&self) -> &crate::MockDatabaseConnection { pub fn as_mock_connection(&self) -> &crate::MockDatabaseConnection {
match self { match self {
DatabaseConnection::MockDatabaseConnection(mock_conn) => mock_conn, 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<crate::Transaction> { pub fn into_transaction_log(self) -> Vec<crate::Transaction> {
let mut mocker = self.as_mock_connection().get_mocker_mutex().lock().unwrap(); let mut mocker = self.as_mock_connection().get_mocker_mutex().lock().unwrap();
mocker.drain_transaction_log() mocker.drain_transaction_log()
@ -223,6 +238,8 @@ impl DatabaseConnection {
} }
impl DbBackend { 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 { pub fn is_prefix_of(self, base_url: &str) -> bool {
let base_url_parsed = Url::parse(base_url).unwrap(); let base_url_parsed = Url::parse(base_url).unwrap();
match self { match self {
@ -234,6 +251,7 @@ impl DbBackend {
} }
} }
/// Build an SQL [Statement]
pub fn build<S>(&self, statement: &S) -> Statement pub fn build<S>(&self, statement: &S) -> Statement
where where
S: StatementBuilder, S: StatementBuilder,
@ -241,6 +259,7 @@ impl DbBackend {
statement.build(self) statement.build(self)
} }
/// A helper for building SQL queries
pub fn get_query_builder(&self) -> Box<dyn QueryBuilder> { pub fn get_query_builder(&self) -> Box<dyn QueryBuilder> {
match self { match self {
Self::MySql => Box::new(MysqlQueryBuilder), Self::MySql => Box::new(MysqlQueryBuilder),

View File

@ -6,6 +6,7 @@ use crate::{
use sea_query::{Value, ValueType, Values}; use sea_query::{Value, ValueType, Values};
use std::{collections::BTreeMap, sync::Arc}; use std::{collections::BTreeMap, sync::Arc};
/// Defines a Mock database suitable for testing
#[derive(Debug)] #[derive(Debug)]
pub struct MockDatabase { pub struct MockDatabase {
db_backend: DbBackend, db_backend: DbBackend,
@ -15,33 +16,44 @@ pub struct MockDatabase {
query_results: Vec<Vec<MockRow>>, query_results: Vec<Vec<MockRow>>,
} }
/// Defines the results obtained from a [MockDatabase]
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct MockExecResult { pub struct MockExecResult {
/// The last inserted id on auto-increment
pub last_insert_id: u64, pub last_insert_id: u64,
/// The number of rows affected by the database operation
pub rows_affected: u64, pub rows_affected: u64,
} }
/// Defines the structure of a test Row for the [MockDatabase]
/// which is just a [BTreeMap]<[String], [Value]>
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct MockRow { pub struct MockRow {
values: BTreeMap<String, Value>, values: BTreeMap<String, Value>,
} }
/// A trait to get a [MockRow] from a type useful for testing in the [MockDatabase]
pub trait IntoMockRow { pub trait IntoMockRow {
/// The method to perform this operation
fn into_mock_row(self) -> MockRow; fn into_mock_row(self) -> MockRow;
} }
/// Defines a transaction that is has not been committed
#[derive(Debug)] #[derive(Debug)]
pub struct OpenTransaction { pub struct OpenTransaction {
stmts: Vec<Statement>, stmts: Vec<Statement>,
transaction_depth: usize, transaction_depth: usize,
} }
/// Defines a database transaction as it holds a Vec<[Statement]>
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Transaction { pub struct Transaction {
stmts: Vec<Statement>, stmts: Vec<Statement>,
} }
impl MockDatabase { impl MockDatabase {
/// Instantiate a mock database with a [DbBackend] to simulate real
/// world SQL databases
pub fn new(db_backend: DbBackend) -> Self { pub fn new(db_backend: DbBackend) -> Self {
Self { Self {
db_backend, db_backend,
@ -52,15 +64,18 @@ impl MockDatabase {
} }
} }
/// Create a database connection
pub fn into_connection(self) -> DatabaseConnection { pub fn into_connection(self) -> DatabaseConnection {
DatabaseConnection::MockDatabaseConnection(Arc::new(MockDatabaseConnection::new(self))) 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<MockExecResult>) -> Self { pub fn append_exec_results(mut self, mut vec: Vec<MockExecResult>) -> Self {
self.exec_results.append(&mut vec); self.exec_results.append(&mut vec);
self self
} }
/// Add the [MockExecResult]s to the `exec_results` field for `Self`
pub fn append_query_results<T>(mut self, vec: Vec<Vec<T>>) -> Self pub fn append_query_results<T>(mut self, vec: Vec<Vec<T>>) -> Self
where where
T: IntoMockRow, T: IntoMockRow,
@ -150,6 +165,7 @@ impl MockDatabaseTrait for MockDatabase {
} }
impl MockRow { impl MockRow {
/// Try to get the values of a [MockRow] and fail gracefully on error
pub fn try_get<T>(&self, col: &str) -> Result<T, DbErr> pub fn try_get<T>(&self, col: &str) -> Result<T, DbErr>
where where
T: ValueType, 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())) 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<Item = (String, Value)> { pub fn into_column_value_tuples(self) -> impl Iterator<Item = (String, Value)> {
self.values.into_iter() self.values.into_iter()
} }
@ -190,6 +207,7 @@ impl IntoMockRow for BTreeMap<&str, Value> {
} }
impl Transaction { impl Transaction {
/// Get the [Value]s from s raw SQL statement depending on the [DatabaseBackend](crate::DatabaseBackend)
pub fn from_sql_and_values<I>(db_backend: DbBackend, sql: &str, values: I) -> Self pub fn from_sql_and_values<I>(db_backend: DbBackend, sql: &str, values: I) -> Self
where where
I: IntoIterator<Item = Value>, I: IntoIterator<Item = Value>,

View File

@ -18,20 +18,30 @@ pub use transaction::*;
use crate::DbErr; use crate::DbErr;
/// Defines a database
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Database; pub struct Database;
/// Defines the configuration options of a database
#[derive(Debug)] #[derive(Debug)]
pub struct ConnectOptions { pub struct ConnectOptions {
/// The URI of the database
pub(crate) url: String, pub(crate) url: String,
/// Maximum number of connections for a pool
pub(crate) max_connections: Option<u32>, pub(crate) max_connections: Option<u32>,
/// Minimum number of connections for a pool
pub(crate) min_connections: Option<u32>, pub(crate) min_connections: Option<u32>,
/// The connection timeout for a packet connection
pub(crate) connect_timeout: Option<Duration>, pub(crate) connect_timeout: Option<Duration>,
/// Maximum idle time for a particular connection to prevent
/// network resource exhaustion
pub(crate) idle_timeout: Option<Duration>, pub(crate) idle_timeout: Option<Duration>,
/// Enables or disables logging
pub(crate) sqlx_logging: bool, pub(crate) sqlx_logging: bool,
} }
impl Database { impl Database {
/// Method to create a [DatabaseConnection] on a database
pub async fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr> pub async fn connect<C>(opt: C) -> Result<DatabaseConnection, DbErr>
where where
C: Into<ConnectOptions>, C: Into<ConnectOptions>,
@ -80,6 +90,7 @@ impl From<String> for ConnectOptions {
} }
impl ConnectOptions { impl ConnectOptions {
/// Create new [ConnectOptions] for a [Database] by passing in a URI string
pub fn new(url: String) -> Self { pub fn new(url: String) -> Self {
Self { Self {
url, url,
@ -122,21 +133,41 @@ impl ConnectOptions {
self self
} }
/// Get the maximum number of connections of the pool, if set
pub fn get_max_connections(&self) -> Option<u32> {
self.max_connections
}
/// Set the minimum number of connections of the pool /// Set the minimum number of connections of the pool
pub fn min_connections(&mut self, value: u32) -> &mut Self { pub fn min_connections(&mut self, value: u32) -> &mut Self {
self.min_connections = Some(value); self.min_connections = Some(value);
self self
} }
/// Get the minimum number of connections of the pool, if set
pub fn get_min_connections(&self) -> Option<u32> {
self.min_connections
}
/// Set the timeout duration when acquiring a connection /// Set the timeout duration when acquiring a connection
pub fn connect_timeout(&mut self, value: Duration) -> &mut Self { pub fn connect_timeout(&mut self, value: Duration) -> &mut Self {
self.connect_timeout = Some(value); self.connect_timeout = Some(value);
self self
} }
/// Get the timeout duration when acquiring a connection, if set
pub fn get_connect_timeout(&self) -> Option<Duration> {
self.connect_timeout
}
/// Set the idle duration before closing a connection /// Set the idle duration before closing a connection
pub fn idle_timeout(&mut self, value: Duration) -> &mut Self { pub fn idle_timeout(&mut self, value: Duration) -> &mut Self {
self.idle_timeout = Some(value); self.idle_timeout = Some(value);
self self
} }
/// Get the idle duration before closing a connection, if set
pub fn get_idle_timeout(&self) -> Option<Duration> {
self.idle_timeout
}
} }

View File

@ -3,18 +3,25 @@ use sea_query::{inject_parameters, MysqlQueryBuilder, PostgresQueryBuilder, Sqli
pub use sea_query::{Value, Values}; pub use sea_query::{Value, Values};
use std::fmt; use std::fmt;
/// Defines an SQL statement
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct Statement { pub struct Statement {
/// The SQL query
pub sql: String, pub sql: String,
/// The values for the SQL statement
pub values: Option<Values>, pub values: Option<Values>,
/// The database backend to use
pub db_backend: DbBackend, pub db_backend: DbBackend,
} }
/// Constraints for building a [Statement]
pub trait StatementBuilder { pub trait StatementBuilder {
/// Method to call in order to build a [Statement]
fn build(&self, db_backend: &DbBackend) -> Statement; fn build(&self, db_backend: &DbBackend) -> Statement;
} }
impl 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 { pub fn from_string(db_backend: DbBackend, stmt: String) -> Statement {
Statement { Statement {
sql: stmt, 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<I>(db_backend: DbBackend, sql: &str, values: I) -> Self pub fn from_sql_and_values<I>(db_backend: DbBackend, sql: &str, values: I) -> Self
where where
I: IntoIterator<Item = Value>, I: IntoIterator<Item = Value>,

View File

@ -12,6 +12,7 @@ use sqlx::{pool::PoolConnection, Executor};
use crate::{DbErr, InnerConnection, QueryResult, Statement}; use crate::{DbErr, InnerConnection, QueryResult, Statement};
/// Creates a stream from a [QueryResult]
#[ouroboros::self_referencing] #[ouroboros::self_referencing]
pub struct QueryStream { pub struct QueryStream {
stmt: Statement, stmt: Statement,

View File

@ -10,6 +10,8 @@ use sqlx::{pool::PoolConnection, TransactionManager};
use std::{future::Future, pin::Pin, sync::Arc}; use std::{future::Future, pin::Pin, sync::Arc};
// a Transaction is just a sugar for a connection where START TRANSACTION has been executed // 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 { pub struct DatabaseTransaction {
conn: Arc<Mutex<InnerConnection>>, conn: Arc<Mutex<InnerConnection>>,
backend: DbBackend, backend: DbBackend,
@ -100,6 +102,8 @@ impl DatabaseTransaction {
Ok(res) 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<F, T, E>(self, callback: F) -> Result<T, TransactionError<E>> pub(crate) async fn run<F, T, E>(self, callback: F) -> Result<T, TransactionError<E>>
where where
F: for<'b> FnOnce( F: for<'b> FnOnce(
@ -120,6 +124,7 @@ impl DatabaseTransaction {
res res
} }
/// Commit a transaction atomically
pub async fn commit(mut self) -> Result<(), DbErr> { pub async fn commit(mut self) -> Result<(), DbErr> {
self.open = false; self.open = false;
match *self.conn.lock().await { match *self.conn.lock().await {
@ -149,6 +154,7 @@ impl DatabaseTransaction {
Ok(()) Ok(())
} }
/// rolls back a transaction in case error are encountered during the operation
pub async fn rollback(mut self) -> Result<(), DbErr> { pub async fn rollback(mut self) -> Result<(), DbErr> {
self.open = false; self.open = false;
match *self.conn.lock().await { match *self.conn.lock().await {
@ -343,12 +349,15 @@ impl<'a> ConnectionTrait<'a> for DatabaseTransaction {
} }
} }
/// Defines errors for handling transaction failures
#[derive(Debug)] #[derive(Debug)]
pub enum TransactionError<E> pub enum TransactionError<E>
where where
E: std::error::Error, E: std::error::Error,
{ {
/// A Database connection error
Connection(DbErr), Connection(DbErr),
/// An error occurring when doing database transactions
Transaction(E), Transaction(E),
} }

View File

@ -12,32 +12,43 @@ use std::{
}, },
}; };
/// Defines a database driver for the [MockDatabase]
#[derive(Debug)] #[derive(Debug)]
pub struct MockDatabaseConnector; pub struct MockDatabaseConnector;
/// Defines a connection for the [MockDatabase]
#[derive(Debug)] #[derive(Debug)]
pub struct MockDatabaseConnection { pub struct MockDatabaseConnection {
counter: AtomicUsize, counter: AtomicUsize,
mocker: Mutex<Box<dyn MockDatabaseTrait>>, mocker: Mutex<Box<dyn MockDatabaseTrait>>,
} }
/// A set of constraints for any type wanting to perform operations on the [MockDatabase]
pub trait MockDatabaseTrait: Send + Debug { pub trait MockDatabaseTrait: Send + Debug {
/// Execute a statement in the [MockDatabase]
fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, DbErr>; fn execute(&mut self, counter: usize, stmt: Statement) -> Result<ExecResult, DbErr>;
/// Execute a SQL query in the [MockDatabase]
fn query(&mut self, counter: usize, stmt: Statement) -> Result<Vec<QueryResult>, DbErr>; fn query(&mut self, counter: usize, stmt: Statement) -> Result<Vec<QueryResult>, DbErr>;
/// Create a transaction that can be committed atomically
fn begin(&mut self); fn begin(&mut self);
/// Commit a successful transaction atomically into the [MockDatabase]
fn commit(&mut self); fn commit(&mut self);
/// Roll back a transaction since errors were encountered
fn rollback(&mut self); fn rollback(&mut self);
/// Get all logs from a [MockDatabase] and return a [Transaction]
fn drain_transaction_log(&mut self) -> Vec<Transaction>; fn drain_transaction_log(&mut self) -> Vec<Transaction>;
/// Get the backend being used in the [MockDatabase]
fn get_database_backend(&self) -> DbBackend; fn get_database_backend(&self) -> DbBackend;
} }
impl MockDatabaseConnector { impl MockDatabaseConnector {
/// Check if the database URI given and the [DatabaseBackend](crate::DatabaseBackend) selected are the same
#[allow(unused_variables)] #[allow(unused_variables)]
pub fn accepts(string: &str) -> bool { pub fn accepts(string: &str) -> bool {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
@ -55,6 +66,7 @@ impl MockDatabaseConnector {
false false
} }
/// Cpnnect to the [MockDatabase]
#[allow(unused_variables)] #[allow(unused_variables)]
pub async fn connect(string: &str) -> Result<DatabaseConnection, DbErr> { pub async fn connect(string: &str) -> Result<DatabaseConnection, DbErr> {
macro_rules! connect_mock_db { macro_rules! connect_mock_db {
@ -82,6 +94,7 @@ impl MockDatabaseConnector {
} }
impl MockDatabaseConnection { impl MockDatabaseConnection {
/// Create a connection to the [MockDatabase]
pub fn new<M: 'static>(m: M) -> Self pub fn new<M: 'static>(m: M) -> Self
where where
M: MockDatabaseTrait, M: MockDatabaseTrait,
@ -96,16 +109,19 @@ impl MockDatabaseConnection {
&self.mocker &self.mocker
} }
/// Get the [DatabaseBackend](crate::DatabaseBackend) being used by the [MockDatabase]
pub fn get_database_backend(&self) -> DbBackend { pub fn get_database_backend(&self) -> DbBackend {
self.mocker.lock().unwrap().get_database_backend() self.mocker.lock().unwrap().get_database_backend()
} }
/// Execute the SQL statement in the [MockDatabase]
pub fn execute(&self, statement: Statement) -> Result<ExecResult, DbErr> { pub fn execute(&self, statement: Statement) -> Result<ExecResult, DbErr> {
debug_print!("{}", statement); debug_print!("{}", statement);
let counter = self.counter.fetch_add(1, Ordering::SeqCst); let counter = self.counter.fetch_add(1, Ordering::SeqCst);
self.mocker.lock().unwrap().execute(counter, statement) self.mocker.lock().unwrap().execute(counter, statement)
} }
/// Return one [QueryResult] if the query was successful
pub fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, DbErr> { pub fn query_one(&self, statement: Statement) -> Result<Option<QueryResult>, DbErr> {
debug_print!("{}", statement); debug_print!("{}", statement);
let counter = self.counter.fetch_add(1, Ordering::SeqCst); let counter = self.counter.fetch_add(1, Ordering::SeqCst);
@ -113,12 +129,14 @@ impl MockDatabaseConnection {
Ok(result.into_iter().next()) Ok(result.into_iter().next())
} }
/// Return all [QueryResult]s if the query was successful
pub fn query_all(&self, statement: Statement) -> Result<Vec<QueryResult>, DbErr> { pub fn query_all(&self, statement: Statement) -> Result<Vec<QueryResult>, DbErr> {
debug_print!("{}", statement); debug_print!("{}", statement);
let counter = self.counter.fetch_add(1, Ordering::SeqCst); let counter = self.counter.fetch_add(1, Ordering::SeqCst);
self.mocker.lock().unwrap().query(counter, statement) self.mocker.lock().unwrap().query(counter, statement)
} }
/// Return [QueryResult]s from a multi-query operation
pub fn fetch( pub fn fetch(
&self, &self,
statement: &Statement, statement: &Statement,
@ -129,14 +147,17 @@ impl MockDatabaseConnection {
} }
} }
/// Create a statement block of SQL statements that execute together.
pub fn begin(&self) { pub fn begin(&self) {
self.mocker.lock().unwrap().begin() self.mocker.lock().unwrap().begin()
} }
/// Commit a transaction atomically to the database
pub fn commit(&self) { pub fn commit(&self) {
self.mocker.lock().unwrap().commit() self.mocker.lock().unwrap().commit()
} }
/// Roll back a faulty transaction
pub fn rollback(&self) { pub fn rollback(&self) {
self.mocker.lock().unwrap().rollback() self.mocker.lock().unwrap().rollback()
} }

View File

@ -1,9 +1,11 @@
use crate::DbErr; use crate::DbErr;
/// Converts an [sqlx::error] execution error to a [DbErr]
pub fn sqlx_error_to_exec_err(err: sqlx::Error) -> DbErr { pub fn sqlx_error_to_exec_err(err: sqlx::Error) -> DbErr {
DbErr::Exec(err.to_string()) 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 { pub fn sqlx_error_to_query_err(err: sqlx::Error) -> DbErr {
DbErr::Query(err.to_string()) DbErr::Query(err.to_string())
} }

View File

@ -15,19 +15,23 @@ use crate::{
use super::sqlx_common::*; use super::sqlx_common::*;
/// Defines the [sqlx::mysql] connector
#[derive(Debug)] #[derive(Debug)]
pub struct SqlxMySqlConnector; pub struct SqlxMySqlConnector;
/// Defines a sqlx MySQL pool
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SqlxMySqlPoolConnection { pub struct SqlxMySqlPoolConnection {
pool: MySqlPool, pool: MySqlPool,
} }
impl SqlxMySqlConnector { impl SqlxMySqlConnector {
/// Check if the URI provided corresponds to `mysql://` for a MySQL database
pub fn accepts(string: &str) -> bool { pub fn accepts(string: &str) -> bool {
string.starts_with("mysql://") && string.parse::<MySqlConnectOptions>().is_ok() string.starts_with("mysql://") && string.parse::<MySqlConnectOptions>().is_ok()
} }
/// Add configuration options for the MySQL database
pub async fn connect(options: ConnectOptions) -> Result<DatabaseConnection, DbErr> { pub async fn connect(options: ConnectOptions) -> Result<DatabaseConnection, DbErr> {
let mut opt = options let mut opt = options
.url .url
@ -48,12 +52,14 @@ impl SqlxMySqlConnector {
} }
impl SqlxMySqlConnector { impl SqlxMySqlConnector {
/// Instantiate a sqlx pool connection to a [DatabaseConnection]
pub fn from_sqlx_mysql_pool(pool: MySqlPool) -> DatabaseConnection { pub fn from_sqlx_mysql_pool(pool: MySqlPool) -> DatabaseConnection {
DatabaseConnection::SqlxMySqlPoolConnection(SqlxMySqlPoolConnection { pool }) DatabaseConnection::SqlxMySqlPoolConnection(SqlxMySqlPoolConnection { pool })
} }
} }
impl SqlxMySqlPoolConnection { impl SqlxMySqlPoolConnection {
/// Execute a [Statement] on a MySQL backend
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> { pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
debug_print!("{}", stmt); 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<Option<QueryResult>, DbErr> { pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
debug_print!("{}", stmt); 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<Vec<QueryResult>, DbErr> { pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
debug_print!("{}", stmt); 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<QueryStream, DbErr> { pub async fn stream(&self, stmt: Statement) -> Result<QueryStream, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
@ -117,6 +126,7 @@ impl SqlxMySqlPoolConnection {
} }
} }
/// Bundle a set of SQL statements that execute together.
pub async fn begin(&self) -> Result<DatabaseTransaction, DbErr> { pub async fn begin(&self) -> Result<DatabaseTransaction, DbErr> {
if let Ok(conn) = self.pool.acquire().await { if let Ok(conn) = self.pool.acquire().await {
DatabaseTransaction::new_mysql(conn).await DatabaseTransaction::new_mysql(conn).await
@ -127,6 +137,7 @@ impl SqlxMySqlPoolConnection {
} }
} }
/// Create a MySQL transaction
pub async fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>> pub async fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>>
where where
F: for<'b> FnOnce( F: for<'b> FnOnce(

View File

@ -15,19 +15,23 @@ use crate::{
use super::sqlx_common::*; use super::sqlx_common::*;
/// Defines the [sqlx::postgres] connector
#[derive(Debug)] #[derive(Debug)]
pub struct SqlxPostgresConnector; pub struct SqlxPostgresConnector;
/// Defines a sqlx PostgreSQL pool
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SqlxPostgresPoolConnection { pub struct SqlxPostgresPoolConnection {
pool: PgPool, pool: PgPool,
} }
impl SqlxPostgresConnector { impl SqlxPostgresConnector {
/// Check if the URI provided corresponds to `postgres://` for a PostgreSQL database
pub fn accepts(string: &str) -> bool { pub fn accepts(string: &str) -> bool {
string.starts_with("postgres://") && string.parse::<PgConnectOptions>().is_ok() string.starts_with("postgres://") && string.parse::<PgConnectOptions>().is_ok()
} }
/// Add configuration options for the MySQL database
pub async fn connect(options: ConnectOptions) -> Result<DatabaseConnection, DbErr> { pub async fn connect(options: ConnectOptions) -> Result<DatabaseConnection, DbErr> {
let mut opt = options let mut opt = options
.url .url
@ -48,12 +52,14 @@ impl SqlxPostgresConnector {
} }
impl SqlxPostgresConnector { impl SqlxPostgresConnector {
/// Instantiate a sqlx pool connection to a [DatabaseConnection]
pub fn from_sqlx_postgres_pool(pool: PgPool) -> DatabaseConnection { pub fn from_sqlx_postgres_pool(pool: PgPool) -> DatabaseConnection {
DatabaseConnection::SqlxPostgresPoolConnection(SqlxPostgresPoolConnection { pool }) DatabaseConnection::SqlxPostgresPoolConnection(SqlxPostgresPoolConnection { pool })
} }
} }
impl SqlxPostgresPoolConnection { impl SqlxPostgresPoolConnection {
/// Execute a [Statement] on a PostgreSQL backend
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> { pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
debug_print!("{}", stmt); 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<Option<QueryResult>, DbErr> { pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
debug_print!("{}", stmt); 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<Vec<QueryResult>, DbErr> { pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
debug_print!("{}", stmt); 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<QueryStream, DbErr> { pub async fn stream(&self, stmt: Statement) -> Result<QueryStream, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
@ -117,6 +126,7 @@ impl SqlxPostgresPoolConnection {
} }
} }
/// Bundle a set of SQL statements that execute together.
pub async fn begin(&self) -> Result<DatabaseTransaction, DbErr> { pub async fn begin(&self) -> Result<DatabaseTransaction, DbErr> {
if let Ok(conn) = self.pool.acquire().await { if let Ok(conn) = self.pool.acquire().await {
DatabaseTransaction::new_postgres(conn).await DatabaseTransaction::new_postgres(conn).await
@ -127,6 +137,7 @@ impl SqlxPostgresPoolConnection {
} }
} }
/// Create a PostgreSQL transaction
pub async fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>> pub async fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>>
where where
F: for<'b> FnOnce( F: for<'b> FnOnce(

View File

@ -15,20 +15,25 @@ use crate::{
use super::sqlx_common::*; use super::sqlx_common::*;
/// Defines the [sqlx::sqlite] connector
#[derive(Debug)] #[derive(Debug)]
pub struct SqlxSqliteConnector; pub struct SqlxSqliteConnector;
/// Defines a sqlx SQLite pool
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SqlxSqlitePoolConnection { pub struct SqlxSqlitePoolConnection {
pool: SqlitePool, pool: SqlitePool,
} }
impl SqlxSqliteConnector { impl SqlxSqliteConnector {
/// Check if the URI provided corresponds to `sqlite:` for a SQLite database
pub fn accepts(string: &str) -> bool { pub fn accepts(string: &str) -> bool {
string.starts_with("sqlite:") && string.parse::<SqliteConnectOptions>().is_ok() string.starts_with("sqlite:") && string.parse::<SqliteConnectOptions>().is_ok()
} }
/// Add configuration options for the SQLite database
pub async fn connect(options: ConnectOptions) -> Result<DatabaseConnection, DbErr> { pub async fn connect(options: ConnectOptions) -> Result<DatabaseConnection, DbErr> {
let mut options = options;
let mut opt = options let mut opt = options
.url .url
.parse::<SqliteConnectOptions>() .parse::<SqliteConnectOptions>()
@ -37,12 +42,10 @@ impl SqlxSqliteConnector {
use sqlx::ConnectOptions; use sqlx::ConnectOptions;
opt.disable_statement_logging(); opt.disable_statement_logging();
} }
if let Ok(pool) = options if options.get_max_connections().is_none() {
.pool_options() options.max_connections(1);
.max_connections(1) }
.connect_with(opt) if let Ok(pool) = options.pool_options().connect_with(opt).await {
.await
{
Ok(DatabaseConnection::SqlxSqlitePoolConnection( Ok(DatabaseConnection::SqlxSqlitePoolConnection(
SqlxSqlitePoolConnection { pool }, SqlxSqlitePoolConnection { pool },
)) ))
@ -53,12 +56,14 @@ impl SqlxSqliteConnector {
} }
impl SqlxSqliteConnector { impl SqlxSqliteConnector {
/// Instantiate a sqlx pool connection to a [DatabaseConnection]
pub fn from_sqlx_sqlite_pool(pool: SqlitePool) -> DatabaseConnection { pub fn from_sqlx_sqlite_pool(pool: SqlitePool) -> DatabaseConnection {
DatabaseConnection::SqlxSqlitePoolConnection(SqlxSqlitePoolConnection { pool }) DatabaseConnection::SqlxSqlitePoolConnection(SqlxSqlitePoolConnection { pool })
} }
} }
impl SqlxSqlitePoolConnection { impl SqlxSqlitePoolConnection {
/// Execute a [Statement] on a SQLite backend
pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> { pub async fn execute(&self, stmt: Statement) -> Result<ExecResult, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
@ -75,6 +80,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<Option<QueryResult>, DbErr> { pub async fn query_one(&self, stmt: Statement) -> Result<Option<QueryResult>, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
@ -94,6 +100,7 @@ impl SqlxSqlitePoolConnection {
} }
} }
/// Get the results of a query returning them as a Vec<[QueryResult]>
pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> { pub async fn query_all(&self, stmt: Statement) -> Result<Vec<QueryResult>, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
@ -110,6 +117,7 @@ impl SqlxSqlitePoolConnection {
} }
} }
/// Stream the results of executing a SQL query
pub async fn stream(&self, stmt: Statement) -> Result<QueryStream, DbErr> { pub async fn stream(&self, stmt: Statement) -> Result<QueryStream, DbErr> {
debug_print!("{}", stmt); debug_print!("{}", stmt);
@ -122,6 +130,7 @@ impl SqlxSqlitePoolConnection {
} }
} }
/// Bundle a set of SQL statements that execute together.
pub async fn begin(&self) -> Result<DatabaseTransaction, DbErr> { pub async fn begin(&self) -> Result<DatabaseTransaction, DbErr> {
if let Ok(conn) = self.pool.acquire().await { if let Ok(conn) = self.pool.acquire().await {
DatabaseTransaction::new_sqlite(conn).await DatabaseTransaction::new_sqlite(conn).await
@ -132,6 +141,7 @@ impl SqlxSqlitePoolConnection {
} }
} }
/// Create a MySQL transaction
pub async fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>> pub async fn transaction<F, T, E>(&self, callback: F) -> Result<T, TransactionError<E>>
where where
F: for<'b> FnOnce( F: for<'b> FnOnce(

View File

@ -5,6 +5,28 @@ use async_trait::async_trait;
use sea_query::{Nullable, ValueTuple}; use sea_query::{Nullable, ValueTuple};
use std::fmt::Debug; use std::fmt::Debug;
/// 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 sea_orm::tests_cfg::{cake, fruit};
/// use sea_orm::{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)] #[derive(Clone, Debug, Default)]
pub struct ActiveValue<V> pub struct ActiveValue<V>
where where
@ -14,6 +36,7 @@ where
state: ActiveValueState, state: ActiveValueState,
} }
/// Defines a set operation on an [ActiveValue]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn Set<V>(v: V) -> ActiveValue<V> pub fn Set<V>(v: V) -> ActiveValue<V>
where where
@ -22,6 +45,7 @@ where
ActiveValue::set(v) ActiveValue::set(v)
} }
/// Defines an unset operation on an [ActiveValue]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn Unset<V>(_: Option<bool>) -> ActiveValue<V> pub fn Unset<V>(_: Option<bool>) -> ActiveValue<V>
where where
@ -30,6 +54,7 @@ where
ActiveValue::unset() ActiveValue::unset()
} }
// Defines the state of an [ActiveValue]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum ActiveValueState { enum ActiveValueState {
Set, Set,
@ -51,22 +76,33 @@ where
ActiveValue::unchanged(value) ActiveValue::unchanged(value)
} }
/// Enforces a set of constraints on any type performing an Create, Update or Delete operation.
/// The type must also implement the [EntityTrait].
/// See module level docs [crate::entity] for a full example
#[async_trait] #[async_trait]
pub trait ActiveModelTrait: Clone + Debug { pub trait ActiveModelTrait: Clone + Debug {
/// Enforce the type to the constraints of the [EntityTrait]
type Entity: EntityTrait; type Entity: EntityTrait;
/// Get a mutable [ActiveValue] from an ActiveModel
fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>; fn take(&mut self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>;
/// Get a immutable [ActiveValue] from an ActiveModel
fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>; fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> ActiveValue<Value>;
/// Set the Value into an ActiveModel
fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value); fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
/// Set the state of an [ActiveValue] to the Unset state
fn unset(&mut self, c: <Self::Entity as EntityTrait>::Column); fn unset(&mut self, c: <Self::Entity as EntityTrait>::Column);
/// Check the state of a [ActiveValue]
fn is_unset(&self, c: <Self::Entity as EntityTrait>::Column) -> bool; fn is_unset(&self, c: <Self::Entity as EntityTrait>::Column) -> bool;
/// The default implementation of the ActiveModel
fn default() -> Self; fn default() -> Self;
/// Get the primary key of the ActiveModel
#[allow(clippy::question_mark)] #[allow(clippy::question_mark)]
fn get_primary_key_value(&self) -> Option<ValueTuple> { fn get_primary_key_value(&self) -> Option<ValueTuple> {
let mut cols = <Self::Entity as EntityTrait>::PrimaryKey::iter(); let mut cols = <Self::Entity as EntityTrait>::PrimaryKey::iter();
@ -103,6 +139,7 @@ pub trait ActiveModelTrait: Clone + Debug {
} }
} }
/// Perform an `INSERT` operation on the ActiveModel
async fn insert<'a, C>(self, db: &'a C) -> Result<Self, DbErr> async fn insert<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
where where
<Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>, <Self::Entity as EntityTrait>::Model: IntoActiveModel<Self>,
@ -121,6 +158,7 @@ pub trait ActiveModelTrait: Clone + Debug {
ActiveModelBehavior::after_save(am, true) ActiveModelBehavior::after_save(am, true)
} }
/// Perform the `UPDATE` operation on an ActiveModel
async fn update<'a, C>(self, db: &'a C) -> Result<Self, DbErr> async fn update<'a, C>(self, db: &'a C) -> Result<Self, DbErr>
where where
Self: ActiveModelBehavior + 'a, Self: ActiveModelBehavior + 'a,
@ -170,7 +208,35 @@ pub trait ActiveModelTrait: Clone + Debug {
} }
} }
/// Behaviors for users to override /// Enforce a set of constraints to a override the ActiveModel behavior
/// Behaviors for users to override.
/// The type must also implement the [ActiveModelTrait]
///
/// ### Example
/// ```ignore
/// 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"
/// }
/// }
///
/// // Derive the ActiveModel
/// #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)]
/// pub struct Model {
/// pub id: i32,
/// pub name: String,
/// }
///
/// impl ActiveModelBehavior for ActiveModel {}
/// ```
/// See module level docs [crate::entity] for a full example
#[allow(unused_variables)] #[allow(unused_variables)]
pub trait ActiveModelBehavior: ActiveModelTrait { pub trait ActiveModelBehavior: ActiveModelTrait {
/// Create a new ActiveModel with default values. Also used by `Default::default()`. /// Create a new ActiveModel with default values. Also used by `Default::default()`.
@ -199,10 +265,12 @@ pub trait ActiveModelBehavior: ActiveModelTrait {
} }
} }
/// Enforce constraints for conversion to an ActiveModel
pub trait IntoActiveModel<A> pub trait IntoActiveModel<A>
where where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
/// Method to call to perform the conversion
fn into_active_model(self) -> A; fn into_active_model(self) -> A;
} }
@ -215,10 +283,12 @@ where
} }
} }
/// Constraints to perform the conversion of a type into an [ActiveValue]
pub trait IntoActiveValue<V> pub trait IntoActiveValue<V>
where where
V: Into<Value>, V: Into<Value>,
{ {
/// Method to perform the conversion
fn into_active_value(self) -> ActiveValue<V>; fn into_active_value(self) -> ActiveValue<V>;
} }
@ -296,6 +366,7 @@ impl<V> ActiveValue<V>
where where
V: Into<Value>, V: Into<Value>,
{ {
/// Set the value of an [ActiveValue] and also set its state to `ActiveValueState::Set`
pub fn set(value: V) -> Self { pub fn set(value: V) -> Self {
Self { Self {
value: Some(value), value: Some(value),
@ -303,6 +374,7 @@ where
} }
} }
/// Check if the state of an [ActiveValue] is `ActiveValueState::Set` which returns true
pub fn is_set(&self) -> bool { pub fn is_set(&self) -> bool {
matches!(self.state, ActiveValueState::Set) matches!(self.state, ActiveValueState::Set)
} }
@ -314,10 +386,14 @@ where
} }
} }
/// Check if the status of the [ActiveValue] is `ActiveValueState::Unchanged`
/// which returns `true` if it is
pub fn is_unchanged(&self) -> bool { pub fn is_unchanged(&self) -> bool {
matches!(self.state, ActiveValueState::Unchanged) 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 { pub fn unset() -> Self {
Self { Self {
value: None, value: None,
@ -325,23 +401,30 @@ where
} }
} }
/// Check if the state of an [ActiveValue] is `ActiveValueState::Unset`
/// which returns true if it is
pub fn is_unset(&self) -> bool { pub fn is_unset(&self) -> bool {
matches!(self.state, ActiveValueState::Unset) 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<V> { pub fn take(&mut self) -> Option<V> {
self.state = ActiveValueState::Unset; self.state = ActiveValueState::Unset;
self.value.take() self.value.take()
} }
/// Get an owned value of the `value` field of the [ActiveValue]
pub fn unwrap(self) -> V { pub fn unwrap(self) -> V {
self.value.unwrap() self.value.unwrap()
} }
/// Check is a [Value] exists or not
pub fn into_value(self) -> Option<Value> { pub fn into_value(self) -> Option<Value> {
self.value.map(Into::into) self.value.map(Into::into)
} }
/// Wrap the [Value] into a `ActiveValue<Value>`
pub fn into_wrapped_value(self) -> ActiveValue<Value> { pub fn into_wrapped_value(self) -> ActiveValue<Value> {
match self.state { match self.state {
ActiveValueState::Set => ActiveValue::set(self.into_value().unwrap()), ActiveValueState::Set => ActiveValue::set(self.into_value().unwrap()),

View File

@ -7,21 +7,28 @@ use sea_query::{Alias, Iden, IntoIden, IntoTableRef, IntoValueTuple, TableRef};
pub use sea_strum::IntoEnumIterator as Iterable; pub use sea_strum::IntoEnumIterator as Iterable;
use std::fmt::Debug; use std::fmt::Debug;
/// Ensure the identifier for an Entity can be converted to a static str
pub trait IdenStatic: Iden + Copy + Debug + 'static { pub trait IdenStatic: Iden + Copy + Debug + 'static {
/// Method to call to get the static string identity
fn as_str(&self) -> &str; fn as_str(&self) -> &str;
} }
/// Enforces the naming of an entity to a set of constraints
pub trait EntityName: IdenStatic + Default { 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> { fn schema_name(&self) -> Option<&str> {
None None
} }
/// Get the name of the table
fn table_name(&self) -> &str; fn table_name(&self) -> &str;
/// Get the name of the module from the invoking `self.table_name()`
fn module_name(&self) -> &str { fn module_name(&self) -> &str {
self.table_name() self.table_name()
} }
/// Get the [TableRef] from invoking the `self.schema_name()`
fn table_ref(&self) -> TableRef { fn table_ref(&self) -> TableRef {
match self.schema_name() { match self.schema_name() {
Some(schema) => (Alias::new(schema).into_iden(), self.into_iden()).into_table_ref(), 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_*` /// - Update: `update`, `update_*`
/// - Delete: `delete`, `delete_*` /// - Delete: `delete`, `delete_*`
pub trait EntityTrait: EntityName { pub trait EntityTrait: EntityName {
#[allow(missing_docs)]
type Model: ModelTrait<Entity = Self> + FromQueryResult; type Model: ModelTrait<Entity = Self> + FromQueryResult;
#[allow(missing_docs)]
type Column: ColumnTrait; type Column: ColumnTrait;
#[allow(missing_docs)]
type Relation: RelationTrait; type Relation: RelationTrait;
#[allow(missing_docs)]
type PrimaryKey: PrimaryKeyTrait + PrimaryKeyToColumn<Column = Self::Column>; type PrimaryKey: PrimaryKeyTrait + PrimaryKeyToColumn<Column = Self::Column>;
/// Check if the relation belongs to an Entity
fn belongs_to<R>(related: R) -> RelationBuilder<Self, R> fn belongs_to<R>(related: R) -> RelationBuilder<Self, R>
where where
R: EntityTrait, R: EntityTrait,
@ -58,6 +70,7 @@ pub trait EntityTrait: EntityName {
RelationBuilder::new(RelationType::HasOne, Self::default(), related, false) RelationBuilder::new(RelationType::HasOne, Self::default(), related, false)
} }
/// Check if the entity has at least one relation
fn has_one<R>(_: R) -> RelationBuilder<Self, R> fn has_one<R>(_: R) -> RelationBuilder<Self, R>
where where
R: EntityTrait + Related<Self>, R: EntityTrait + Related<Self>,
@ -65,6 +78,7 @@ pub trait EntityTrait: EntityName {
RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true) RelationBuilder::from_rel(RelationType::HasOne, R::to().rev(), true)
} }
/// Check if the Entity has many relations
fn has_many<R>(_: R) -> RelationBuilder<Self, R> fn has_many<R>(_: R) -> RelationBuilder<Self, R>
where where
R: EntityTrait + Related<Self>, R: EntityTrait + Related<Self>,

View File

@ -2,6 +2,7 @@ use crate::{EntityName, IdenStatic, Iterable};
use sea_query::{DynIden, Expr, SeaRc, SelectStatement, SimpleExpr, Value}; use sea_query::{DynIden, Expr, SeaRc, SelectStatement, SimpleExpr, Value};
use std::str::FromStr; use std::str::FromStr;
/// Defines a Column for an Entity
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ColumnDef { pub struct ColumnDef {
pub(crate) col_type: ColumnType, pub(crate) col_type: ColumnType,
@ -10,35 +11,63 @@ pub struct ColumnDef {
pub(crate) indexed: bool, pub(crate) indexed: bool,
} }
/// The type of column as defined in the SQL format
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum ColumnType { pub enum ColumnType {
/// `CHAR` type of specified fixed length
Char(Option<u32>), Char(Option<u32>),
/// `STRING` type for variable string length
String(Option<u32>), String(Option<u32>),
/// `TEXT` type used for large pieces of string data and stored out of row in case size is too big
Text, Text,
/// `TINYINT` useful for storing one byte of data (range of 0-255)
TinyInteger, TinyInteger,
/// `SMALLINT` data type stores small whole numbers that range from 32,767 to 32,767
SmallInteger, SmallInteger,
/// `INTEGER` data types hold numbers that are whole, or without a decimal point
Integer, 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, BigInteger,
/// `FLOAT` an approximate-number data type, where values range cannot be represented exactly.
Float, Float,
/// `DOUBLE` is a normal-size floating point number where the
/// total number of digits is specified in size.
Double, Double,
/// `DECIMAL` type store numbers that have fixed precision and scale
Decimal(Option<(u32, u32)>), Decimal(Option<(u32, u32)>),
/// `DATETIME` type is used for values that contain both date and time parts.
DateTime, DateTime,
/// `TIMESTAMP` is a temporal data type that holds the combination of date and time.
Timestamp, 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, TimestampWithTimeZone,
/// `TIME` data type defines a time of a day based on 24-hour clock
Time, Time,
/// `DATE` data type stores the calendar date
Date, Date,
/// `BINARY` data types contain byte strings—a sequence of octets or bytes.
Binary, Binary,
/// `BOOLEAN` is the result of a comparison operator
Boolean, Boolean,
/// `MONEY` data type handles monetary data
Money(Option<(u32, u32)>), Money(Option<(u32, u32)>),
/// `JSON` represents the JavaScript Object Notation type
Json, 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, JsonBinary,
/// A custom implementation of a data type
Custom(String), Custom(String),
/// A Universally Unique IDentifier that is specified in RFC 4122
Uuid, Uuid,
Enum(String, Vec<String>), Enum(String, Vec<String>),
} }
macro_rules! bind_oper { macro_rules! bind_oper {
( $op: ident ) => { ( $op: ident ) => {
#[allow(missing_docs)]
fn $op<V>(&self, v: V) -> SimpleExpr fn $op<V>(&self, v: V) -> SimpleExpr
where where
V: Into<Value>, V: Into<Value>,
@ -59,6 +88,7 @@ macro_rules! bind_func_no_params {
macro_rules! bind_vec_func { macro_rules! bind_vec_func {
( $func: ident ) => { ( $func: ident ) => {
#[allow(missing_docs)]
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
fn $func<V, I>(&self, v: I) -> SimpleExpr fn $func<V, I>(&self, v: I) -> SimpleExpr
where where
@ -73,6 +103,7 @@ macro_rules! bind_vec_func {
macro_rules! bind_subquery_func { macro_rules! bind_subquery_func {
( $func: ident ) => { ( $func: ident ) => {
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
#[allow(missing_docs)]
fn $func(&self, s: SelectStatement) -> SimpleExpr { fn $func(&self, s: SelectStatement) -> SimpleExpr {
Expr::tbl(self.entity_name(), *self).$func(s) Expr::tbl(self.entity_name(), *self).$func(s)
} }
@ -82,14 +113,18 @@ macro_rules! bind_subquery_func {
// LINT: when the operand value does not match column type // LINT: when the operand value does not match column type
/// Wrapper of the identically named method in [`sea_query::Expr`] /// Wrapper of the identically named method in [`sea_query::Expr`]
pub trait ColumnTrait: IdenStatic + Iterable + FromStr { pub trait ColumnTrait: IdenStatic + Iterable + FromStr {
#[allow(missing_docs)]
type EntityName: EntityName; type EntityName: EntityName;
/// Define a column for an Entity
fn def(&self) -> ColumnDef; fn def(&self) -> ColumnDef;
/// Get the name of the entity the column belongs to
fn entity_name(&self) -> DynIden { fn entity_name(&self) -> DynIden {
SeaRc::new(Self::EntityName::default()) as 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) { fn as_column_ref(&self) -> (DynIden, DynIden) {
(self.entity_name(), SeaRc::new(*self) as DynIden) (self.entity_name(), SeaRc::new(*self) as DynIden)
} }
@ -222,6 +257,7 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr {
bind_func_no_params!(is_null); bind_func_no_params!(is_null);
bind_func_no_params!(is_not_null); bind_func_no_params!(is_not_null);
/// Perform an operation if the column is null
fn if_null<V>(&self, v: V) -> SimpleExpr fn if_null<V>(&self, v: V) -> SimpleExpr
where where
V: Into<Value>, V: Into<Value>,
@ -237,6 +273,7 @@ pub trait ColumnTrait: IdenStatic + Iterable + FromStr {
} }
impl ColumnType { impl ColumnType {
/// instantiate a new [ColumnDef]
pub fn def(self) -> ColumnDef { pub fn def(self) -> ColumnDef {
ColumnDef { ColumnDef {
col_type: self, col_type: self,
@ -255,20 +292,24 @@ impl ColumnType {
} }
impl ColumnDef { impl ColumnDef {
/// Marks the column as `UNIQUE`
pub fn unique(mut self) -> Self { pub fn unique(mut self) -> Self {
self.unique = true; self.unique = true;
self self
} }
/// Mark the column as nullable
pub fn null(self) -> Self { pub fn null(self) -> Self {
self.nullable() self.nullable()
} }
/// Mark the column as nullable
pub fn nullable(mut self) -> Self { pub fn nullable(mut self) -> Self {
self.null = true; self.null = true;
self self
} }
/// Set the `indexed` field to `true`
pub fn indexed(mut self) -> Self { pub fn indexed(mut self) -> Self {
self.indexed = true; self.indexed = true;
self self

View File

@ -2,10 +2,14 @@ use crate::{ColumnTrait, EntityTrait, IdenStatic};
use sea_query::{Alias, DynIden, Iden, IntoIden, SeaRc}; use sea_query::{Alias, DynIden, Iden, IntoIden, SeaRc};
use std::fmt; use std::fmt;
/// Defines an operation for an Entity
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Identity { pub enum Identity {
/// Performs one operation
Unary(DynIden), Unary(DynIden),
/// Performs two operations
Binary(DynIden, DynIden), Binary(DynIden, DynIden),
/// Performs three operations
Ternary(DynIden, DynIden, DynIden), Ternary(DynIden, DynIden, DynIden),
} }
@ -28,14 +32,18 @@ impl Iden for Identity {
} }
} }
/// Performs a conversion into an [Identity]
pub trait IntoIdentity { pub trait IntoIdentity {
/// Method to perform the conversion
fn into_identity(self) -> Identity; fn into_identity(self) -> Identity;
} }
/// Check the [Identity] of an Entity
pub trait IdentityOf<E> pub trait IdentityOf<E>
where where
E: EntityTrait, E: EntityTrait,
{ {
/// Method to call to perform this check
fn identity_of(self) -> Identity; fn identity_of(self) -> Identity;
} }

View File

@ -3,15 +3,21 @@ use crate::{
}; };
use sea_query::{Alias, IntoIden, JoinType, SeaRc}; use sea_query::{Alias, IntoIden, JoinType, SeaRc};
/// Same as [RelationDef]
pub type LinkDef = RelationDef; pub type LinkDef = RelationDef;
/// A set of constraints for links between Entities
pub trait Linked { pub trait Linked {
#[allow(missing_docs)]
type FromEntity: EntityTrait; type FromEntity: EntityTrait;
#[allow(missing_docs)]
type ToEntity: EntityTrait; type ToEntity: EntityTrait;
/// Link for an Entity
fn link(&self) -> Vec<LinkDef>; fn link(&self) -> Vec<LinkDef>;
/// Find all the Entities that are linked to the Entity
fn find_linked(&self) -> Select<Self::ToEntity> { fn find_linked(&self) -> Select<Self::ToEntity> {
let mut select = Select::new(); let mut select = Select::new();
for (i, rel) in self.link().into_iter().rev().enumerate() { for (i, rel) in self.link().into_iter().rev().enumerate() {

View File

@ -1,3 +1,103 @@
/// 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 sea_orm::entity::prelude::*;
/// 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_enum; mod active_enum;
mod active_model; mod active_model;
mod base_entity; mod base_entity;
@ -5,6 +105,7 @@ mod column;
mod identity; mod identity;
mod link; mod link;
mod model; mod model;
/// Re-export common types from the entity
pub mod prelude; pub mod prelude;
mod primary_key; mod primary_key;
mod relation; mod relation;

View File

@ -5,13 +5,18 @@ use crate::{
pub use sea_query::Value; pub use sea_query::Value;
use std::fmt::Debug; use std::fmt::Debug;
/// A set of constraints for a Model
pub trait ModelTrait: Clone + Send + Debug { pub trait ModelTrait: Clone + Send + Debug {
#[allow(missing_docs)]
type Entity: EntityTrait; type Entity: EntityTrait;
/// Get the [Value] of a column from an Entity
fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> Value; fn get(&self, c: <Self::Entity as EntityTrait>::Column) -> Value;
/// Set the [Value] of a column in an Entity
fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value); fn set(&mut self, c: <Self::Entity as EntityTrait>::Column, v: Value);
/// Find related Models
fn find_related<R>(&self, _: R) -> Select<R> fn find_related<R>(&self, _: R) -> Select<R>
where where
R: EntityTrait, R: EntityTrait,
@ -20,6 +25,7 @@ pub trait ModelTrait: Clone + Send + Debug {
<Self::Entity as Related<R>>::find_related().belongs_to(self) <Self::Entity as Related<R>>::find_related().belongs_to(self)
} }
/// Find linked Models
fn find_linked<L>(&self, l: L) -> Select<L::ToEntity> fn find_linked<L>(&self, l: L) -> Select<L::ToEntity>
where where
L: Linked<FromEntity = Self::Entity>, L: Linked<FromEntity = Self::Entity>,
@ -29,9 +35,13 @@ pub trait ModelTrait: Clone + Send + Debug {
} }
} }
/// A set of constraints for implementing a [QueryResult]
pub trait FromQueryResult: Sized { pub trait FromQueryResult: Sized {
/// Instantiate a Model from a [QueryResult]
fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr>; fn from_query_result(res: &QueryResult, pre: &str) -> Result<Self, DbErr>;
/// 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<Option<Self>, DbErr> { fn from_query_result_optional(res: &QueryResult, pre: &str) -> Result<Option<Self>, DbErr> {
Ok(Self::from_query_result(res, pre).ok()) Ok(Self::from_query_result(res, pre).ok())
} }

View File

@ -24,6 +24,7 @@ pub use chrono::NaiveTime as Time;
#[cfg(feature = "with-chrono")] #[cfg(feature = "with-chrono")]
pub use chrono::NaiveDateTime as DateTime; pub use chrono::NaiveDateTime as DateTime;
/// Handles the time and dates
#[cfg(feature = "with-chrono")] #[cfg(feature = "with-chrono")]
pub type DateTimeWithTimeZone = chrono::DateTime<chrono::FixedOffset>; pub type DateTimeWithTimeZone = chrono::DateTime<chrono::FixedOffset>;

View File

@ -4,7 +4,41 @@ use sea_query::{FromValueTuple, IntoValueTuple};
use std::fmt::Debug; use std::fmt::Debug;
//LINT: composite primary key cannot auto increment //LINT: composite primary key cannot auto increment
/// A set of constraints to be used to define a Primary Key.
///
/// A primary key can be derived manually
///
/// ### Example
/// ```text
/// 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
/// ```text
/// use sea_orm::entity::prelude::*;
///
/// #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
/// pub enum PrimaryKey {
/// Id,
/// }
/// ```
/// See module level docs [crate::entity] for a full example
pub trait PrimaryKeyTrait: IdenStatic + Iterable { pub trait PrimaryKeyTrait: IdenStatic + Iterable {
#[allow(missing_docs)]
type ValueType: Sized type ValueType: Sized
+ Send + Send
+ Debug + Debug
@ -14,14 +48,19 @@ pub trait PrimaryKeyTrait: IdenStatic + Iterable {
+ TryGetableMany + TryGetableMany
+ TryFromU64; + TryFromU64;
/// Method to call to perform `AUTOINCREMENT` operation on a Primary Kay
fn auto_increment() -> bool; fn auto_increment() -> bool;
} }
/// How to map a Primary Key to a column
pub trait PrimaryKeyToColumn { pub trait PrimaryKeyToColumn {
#[allow(missing_docs)]
type Column: ColumnTrait; type Column: ColumnTrait;
/// Method to map a primary key to a column in an Entity
fn into_column(self) -> Self::Column; 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<Self> fn from_column(col: Self::Column) -> Option<Self>
where where
Self: Sized; Self: Sized;

View File

@ -3,45 +3,68 @@ use core::marker::PhantomData;
use sea_query::{JoinType, TableRef}; use sea_query::{JoinType, TableRef};
use std::fmt::Debug; use std::fmt::Debug;
/// Defines the type of relationship
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum RelationType { pub enum RelationType {
/// An Entity has one relationship
HasOne, HasOne,
/// An Entity has many relationships
HasMany, HasMany,
} }
/// Action to perform on a foreign key whenever there are changes
/// to an ActiveModel
pub type ForeignKeyAction = sea_query::ForeignKeyAction; pub type ForeignKeyAction = sea_query::ForeignKeyAction;
/// Constraints a type to implement the trait to create a relationship
pub trait RelationTrait: Iterable + Debug + 'static { pub trait RelationTrait: Iterable + Debug + 'static {
/// The method to call
fn def(&self) -> RelationDef; fn def(&self) -> RelationDef;
} }
/// Checks if Entities are related
pub trait Related<R> pub trait Related<R>
where where
R: EntityTrait, R: EntityTrait,
{ {
/// Check if an entity is related to another entity
fn to() -> RelationDef; fn to() -> RelationDef;
/// Check if an entity is related through another entity
fn via() -> Option<RelationDef> { fn via() -> Option<RelationDef> {
None None
} }
/// Find related Entities
fn find_related() -> Select<R> { fn find_related() -> Select<R> {
Select::<R>::new().join_join_rev(JoinType::InnerJoin, Self::to(), Self::via()) Select::<R>::new().join_join_rev(JoinType::InnerJoin, Self::to(), Self::via())
} }
} }
/// Defines a relationship
#[derive(Debug)] #[derive(Debug)]
pub struct RelationDef { pub struct RelationDef {
/// The type of relationship defined in [RelationType]
pub rel_type: RelationType, pub rel_type: RelationType,
/// Reference from another Entity
pub from_tbl: TableRef, pub from_tbl: TableRef,
/// Reference to another ENtity
pub to_tbl: TableRef, pub to_tbl: TableRef,
/// Reference to from a Column
pub from_col: Identity, pub from_col: Identity,
/// Reference to another column
pub to_col: Identity, pub to_col: Identity,
/// Defines the owner of the Relation
pub is_owner: bool, pub is_owner: bool,
/// Defines an operation to be performed on a Foreign Key when a
/// `DELETE` Operation is performed
pub on_delete: Option<ForeignKeyAction>, pub on_delete: Option<ForeignKeyAction>,
/// Defines an operation to be performed on a Foreign Key when a
/// `UPDATE` Operation is performed
pub on_update: Option<ForeignKeyAction>, pub on_update: Option<ForeignKeyAction>,
} }
/// Defines a helper to build a relation
#[derive(Debug)] #[derive(Debug)]
pub struct RelationBuilder<E, R> pub struct RelationBuilder<E, R>
where where
@ -108,6 +131,7 @@ where
} }
} }
/// Build a relationship from an Entity
pub fn from<T>(mut self, identifier: T) -> Self pub fn from<T>(mut self, identifier: T) -> Self
where where
T: IdentityOf<E>, T: IdentityOf<E>,
@ -116,6 +140,7 @@ where
self self
} }
/// Build a relationship to an Entity
pub fn to<T>(mut self, identifier: T) -> Self pub fn to<T>(mut self, identifier: T) -> Self
where where
T: IdentityOf<R>, T: IdentityOf<R>,
@ -124,11 +149,13 @@ where
self self
} }
/// An operation to perform on a foreign key when a delete operation occurs
pub fn on_delete(mut self, action: ForeignKeyAction) -> Self { pub fn on_delete(mut self, action: ForeignKeyAction) -> Self {
self.on_delete = Some(action); self.on_delete = Some(action);
self self
} }
/// An operation to perform on a foreign key when an update operation occurs
pub fn on_update(mut self, action: ForeignKeyAction) -> Self { pub fn on_update(mut self, action: ForeignKeyAction) -> Self {
self.on_update = Some(action); self.on_update = Some(action);
self self

View File

@ -1,21 +1,16 @@
/// Represents all the errors in SeaORM. /// An error from unsuccessful database operations
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum DbErr { pub enum DbErr {
/// Error occurred while connecting to database engine. /// There was a problem with the database connection
Conn(String), Conn(String),
/// An operation did not execute successfully
/// Error occurred while executing SQL statement.
Exec(String), Exec(String),
/// An error occurred while performing a query
/// Error occurred while querying SQL statement.
Query(String), Query(String),
/// The record was not found in the database
/// Error occurred while updating a non-existing row in database.
RecordNotFound(String), RecordNotFound(String),
/// A custom error
/// Error occurred while performing custom validation logics in [ActiveModelBehavior](crate::ActiveModelBehavior)
Custom(String), Custom(String),
/// Error occurred while parsing value into [ActiveEnum](crate::ActiveEnum) /// Error occurred while parsing value into [ActiveEnum](crate::ActiveEnum)
Type(String), Type(String),
} }
@ -35,6 +30,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)] #[derive(Debug, Clone)]
pub struct ColumnFromStrErr(pub String); pub struct ColumnFromStrErr(pub String);

View File

@ -4,13 +4,16 @@ use crate::{
use sea_query::DeleteStatement; use sea_query::DeleteStatement;
use std::future::Future; use std::future::Future;
/// Handles DELETE operations in a ActiveModel using [DeleteStatement]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Deleter { pub struct Deleter {
query: DeleteStatement, query: DeleteStatement,
} }
/// The result of a DELETE operation
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DeleteResult { pub struct DeleteResult {
/// The number of rows affected by the DELETE operation
pub rows_affected: u64, pub rows_affected: u64,
} }
@ -18,6 +21,7 @@ impl<'a, A: 'a> DeleteOne<A>
where where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
/// Execute a DELETE operation on one ActiveModel
pub fn exec<C>(self, db: &'a C) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_ pub fn exec<C>(self, db: &'a C) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -31,6 +35,7 @@ impl<'a, E> DeleteMany<E>
where where
E: EntityTrait, E: EntityTrait,
{ {
/// Execute a DELETE operation on many ActiveModels
pub fn exec<C>(self, db: &'a C) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_ pub fn exec<C>(self, db: &'a C) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -41,10 +46,12 @@ where
} }
impl Deleter { impl Deleter {
/// Instantiate a new [Deleter] by passing it a [DeleteStatement]
pub fn new(query: DeleteStatement) -> Self { pub fn new(query: DeleteStatement) -> Self {
Self { query } Self { query }
} }
/// Execute a DELETE operation
pub fn exec<'a, C>(self, db: &'a C) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_ pub fn exec<'a, C>(self, db: &'a C) -> impl Future<Output = Result<DeleteResult, DbErr>> + '_
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,

View File

@ -1,16 +1,24 @@
/// Defines the result of executing an operation
#[derive(Debug)] #[derive(Debug)]
pub struct ExecResult { 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, pub(crate) result: ExecResultHolder,
} }
/// Holds a result depending on the database backend chosen by the feature flag
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum ExecResultHolder { pub(crate) enum ExecResultHolder {
/// Holds the result of executing an operation on a MySQL database
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]
SqlxMySql(sqlx::mysql::MySqlQueryResult), SqlxMySql(sqlx::mysql::MySqlQueryResult),
/// Holds the result of executing an operation on a PostgreSQL database
#[cfg(feature = "sqlx-postgres")] #[cfg(feature = "sqlx-postgres")]
SqlxPostgres(sqlx::postgres::PgQueryResult), SqlxPostgres(sqlx::postgres::PgQueryResult),
/// Holds the result of executing an operation on a SQLite database
#[cfg(feature = "sqlx-sqlite")] #[cfg(feature = "sqlx-sqlite")]
SqlxSqlite(sqlx::sqlite::SqliteQueryResult), SqlxSqlite(sqlx::sqlite::SqliteQueryResult),
/// Holds the result of executing an operation on the Mock database
#[cfg(feature = "mock")] #[cfg(feature = "mock")]
Mock(crate::MockExecResult), Mock(crate::MockExecResult),
} }
@ -18,6 +26,7 @@ pub(crate) enum ExecResultHolder {
// ExecResult // // ExecResult //
impl ExecResult { impl ExecResult {
/// Get the last id after `AUTOINCREMENT` is done on the primary key
pub fn last_insert_id(&self) -> u64 { pub fn last_insert_id(&self) -> u64 {
match &self.result { match &self.result {
#[cfg(feature = "sqlx-mysql")] #[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 { pub fn rows_affected(&self) -> u64 {
match &self.result { match &self.result {
#[cfg(feature = "sqlx-mysql")] #[cfg(feature = "sqlx-mysql")]

View File

@ -5,6 +5,7 @@ use crate::{
use sea_query::{FromValueTuple, InsertStatement, ValueTuple}; use sea_query::{FromValueTuple, InsertStatement, ValueTuple};
use std::{future::Future, marker::PhantomData}; use std::{future::Future, marker::PhantomData};
/// Defines a structure to perform INSERT operations in an ActiveModel
#[derive(Debug)] #[derive(Debug)]
pub struct Inserter<A> pub struct Inserter<A>
where where
@ -15,11 +16,13 @@ where
model: PhantomData<A>, model: PhantomData<A>,
} }
/// The result of an INSERT operation on an ActiveModel
#[derive(Debug)] #[derive(Debug)]
pub struct InsertResult<A> pub struct InsertResult<A>
where where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
/// The id performed when AUTOINCREMENT was performed on the PrimaryKey
pub last_insert_id: <<<A as ActiveModelTrait>::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType, pub last_insert_id: <<<A as ActiveModelTrait>::Entity as EntityTrait>::PrimaryKey as PrimaryKeyTrait>::ValueType,
} }
@ -27,6 +30,7 @@ impl<A> Insert<A>
where where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
/// Execute an insert operation
#[allow(unused_mut)] #[allow(unused_mut)]
pub fn exec<'a, C>(self, db: &'a C) -> impl Future<Output = Result<InsertResult<A>, DbErr>> + '_ pub fn exec<'a, C>(self, db: &'a C) -> impl Future<Output = Result<InsertResult<A>, DbErr>> + '_
where where
@ -53,6 +57,7 @@ impl<A> Inserter<A>
where where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
/// Instantiate a new insert operation
pub fn new(primary_key: Option<ValueTuple>, query: InsertStatement) -> Self { pub fn new(primary_key: Option<ValueTuple>, query: InsertStatement) -> Self {
Self { Self {
primary_key, primary_key,
@ -61,6 +66,7 @@ where
} }
} }
/// Execute an insert operation
pub fn exec<'a, C>(self, db: &'a C) -> impl Future<Output = Result<InsertResult<A>, DbErr>> + '_ pub fn exec<'a, C>(self, db: &'a C) -> impl Future<Output = Result<InsertResult<A>, DbErr>> + '_
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,

View File

@ -4,8 +4,10 @@ use futures::Stream;
use sea_query::{Alias, Expr, SelectStatement}; use sea_query::{Alias, Expr, SelectStatement};
use std::{marker::PhantomData, pin::Pin}; 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<Box<dyn Stream<Item = Item> + 'db>>; pub type PinBoxStream<'db, Item> = Pin<Box<dyn Stream<Item = Item> + 'db>>;
/// Defined a structure to handle pagination of a result from a query operation on a Model
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Paginator<'db, C, S> pub struct Paginator<'db, C, S>
where where

View File

@ -3,6 +3,7 @@ use crate::debug_print;
use crate::{DbErr, SelectGetableValue, SelectorRaw, Statement}; use crate::{DbErr, SelectGetableValue, SelectorRaw, Statement};
use std::fmt; use std::fmt;
/// Defines the result of a query operation on a Model
#[derive(Debug)] #[derive(Debug)]
pub struct QueryResult { pub struct QueryResult {
pub(crate) row: QueryResultRow, pub(crate) row: QueryResultRow,
@ -19,13 +20,18 @@ pub(crate) enum QueryResultRow {
Mock(crate::MockRow), Mock(crate::MockRow),
} }
/// Constrain any type trying to get a Row in a database
pub trait TryGetable: Sized { pub trait TryGetable: Sized {
/// Ensure the type implements this method
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError>; fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError>;
} }
/// An error from trying to get a row from a Model
#[derive(Debug)] #[derive(Debug)]
pub enum TryGetError { pub enum TryGetError {
/// A database error was encountered as defined in [crate::DbErr]
DbErr(DbErr), DbErr(DbErr),
/// A null value was encountered
Null, Null,
} }
@ -41,6 +47,7 @@ impl From<TryGetError> for DbErr {
// QueryResult // // QueryResult //
impl QueryResult { impl QueryResult {
/// Get a Row from a Column
pub fn try_get<T>(&self, pre: &str, col: &str) -> Result<T, DbErr> pub fn try_get<T>(&self, pre: &str, col: &str) -> Result<T, DbErr>
where where
T: TryGetable, T: TryGetable,
@ -48,6 +55,7 @@ impl QueryResult {
Ok(T::try_get(self, pre, col)?) Ok(T::try_get(self, pre, col)?)
} }
/// Perform query operations on multiple Columns
pub fn try_get_many<T>(&self, pre: &str, cols: &[String]) -> Result<T, DbErr> pub fn try_get_many<T>(&self, pre: &str, cols: &[String]) -> Result<T, DbErr>
where where
T: TryGetableMany, T: TryGetableMany,
@ -306,7 +314,9 @@ try_getable_all!(uuid::Uuid);
// TryGetableMany // // TryGetableMany //
/// Perform a query on multiple columns
pub trait TryGetableMany: Sized { pub trait TryGetableMany: Sized {
/// THe method to perform a query on multiple columns
fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>; fn try_get_many(res: &QueryResult, pre: &str, cols: &[String]) -> Result<Self, TryGetError>;
/// ``` /// ```
@ -453,8 +463,9 @@ fn try_get_many_with_slice_len_of(len: usize, cols: &[String]) -> Result<(), Try
} }
// TryFromU64 // // TryFromU64 //
/// Try to convert a type to a u64
pub trait TryFromU64: Sized { pub trait TryFromU64: Sized {
/// The method to convert the type to a u64
fn try_from_u64(n: u64) -> Result<Self, DbErr>; fn try_from_u64(n: u64) -> Result<Self, DbErr>;
} }

View File

@ -11,6 +11,7 @@ use std::pin::Pin;
#[cfg(feature = "with-json")] #[cfg(feature = "with-json")]
use crate::JsonValue; use crate::JsonValue;
/// Defines a type to do `SELECT` operations though a [SelectStatement] on a Model
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Selector<S> pub struct Selector<S>
where where
@ -20,6 +21,7 @@ where
selector: S, selector: S,
} }
/// Performs a raw `SELECT` operation on a model
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SelectorRaw<S> pub struct SelectorRaw<S>
where where
@ -29,12 +31,16 @@ where
selector: S, selector: S,
} }
/// Used to enforce constraints on any type that wants to perform SELECT queries
pub trait SelectorTrait { pub trait SelectorTrait {
#[allow(missing_docs)]
type Item: Sized; type Item: Sized;
/// The method to perform a query on a Model
fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr>; fn from_raw_query_result(res: QueryResult) -> Result<Self::Item, DbErr>;
} }
/// Perform an operation on an entity that can yield a Value
#[derive(Debug)] #[derive(Debug)]
pub struct SelectGetableValue<T, C> pub struct SelectGetableValue<T, C>
where where
@ -45,6 +51,7 @@ where
model: PhantomData<T>, model: PhantomData<T>,
} }
/// Defines a type to get a Model
#[derive(Debug)] #[derive(Debug)]
pub struct SelectModel<M> pub struct SelectModel<M>
where where
@ -53,6 +60,7 @@ where
model: PhantomData<M>, model: PhantomData<M>,
} }
/// Defines a type to get two Modelss
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SelectTwoModel<M, N> pub struct SelectTwoModel<M, N>
where where
@ -105,6 +113,7 @@ impl<E> Select<E>
where where
E: EntityTrait, E: EntityTrait,
{ {
/// Perform a Select operation on a Model using a [Statement]
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub fn from_raw_sql(self, stmt: Statement) -> SelectorRaw<SelectModel<E::Model>> { pub fn from_raw_sql(self, stmt: Statement) -> SelectorRaw<SelectModel<E::Model>> {
SelectorRaw { SelectorRaw {
@ -113,6 +122,7 @@ where
} }
} }
/// Return a [Selector] from `Self` that wraps a [SelectModel]
pub fn into_model<M>(self) -> Selector<SelectModel<M>> pub fn into_model<M>(self) -> Selector<SelectModel<M>>
where where
M: FromQueryResult, M: FromQueryResult,
@ -123,6 +133,7 @@ where
} }
} }
/// Get a selectable Model as a [JsonValue] for SQL JSON operations
#[cfg(feature = "with-json")] #[cfg(feature = "with-json")]
pub fn into_json(self) -> Selector<SelectModel<JsonValue>> { pub fn into_json(self) -> Selector<SelectModel<JsonValue>> {
Selector { Selector {
@ -239,6 +250,7 @@ where
Selector::<SelectGetableValue<T, C>>::with_columns(self.query) Selector::<SelectGetableValue<T, C>>::with_columns(self.query)
} }
/// Get one Model from a SELECT operation
pub async fn one<'a, C>(self, db: &C) -> Result<Option<E::Model>, DbErr> pub async fn one<'a, C>(self, db: &C) -> Result<Option<E::Model>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -246,6 +258,7 @@ where
self.into_model().one(db).await self.into_model().one(db).await
} }
/// Get all the Models from a SELECT operation
pub async fn all<'a, C>(self, db: &C) -> Result<Vec<E::Model>, DbErr> pub async fn all<'a, C>(self, db: &C) -> Result<Vec<E::Model>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -253,6 +266,7 @@ where
self.into_model().all(db).await self.into_model().all(db).await
} }
/// Stream the results of a SELECT operation on a Model
pub async fn stream<'a: 'b, 'b, C>( pub async fn stream<'a: 'b, 'b, C>(
self, self,
db: &'a C, db: &'a C,
@ -263,6 +277,7 @@ where
self.into_model().stream(db).await self.into_model().stream(db).await
} }
/// Paginate the results of a SELECT operation on a Model
pub fn paginate<'a, C>( pub fn paginate<'a, C>(
self, self,
db: &'a C, db: &'a C,
@ -274,6 +289,7 @@ where
self.into_model().paginate(db, page_size) 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<usize, DbErr> pub async fn count<'a, C>(self, db: &'a C) -> Result<usize, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -287,6 +303,7 @@ where
E: EntityTrait, E: EntityTrait,
F: EntityTrait, F: EntityTrait,
{ {
/// Perform a conversion into a [SelectTwoModel]
pub fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>> pub fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
where where
M: FromQueryResult, M: FromQueryResult,
@ -298,6 +315,7 @@ where
} }
} }
/// Convert the Models into JsonValue
#[cfg(feature = "with-json")] #[cfg(feature = "with-json")]
pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> { pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> {
Selector { Selector {
@ -306,6 +324,7 @@ where
} }
} }
/// Get one Model from a Select operation
pub async fn one<'a, C>(self, db: &C) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr> pub async fn one<'a, C>(self, db: &C) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -313,6 +332,7 @@ where
self.into_model().one(db).await self.into_model().one(db).await
} }
/// Get all Models from a Select operation
pub async fn all<'a, C>(self, db: &C) -> Result<Vec<(E::Model, Option<F::Model>)>, DbErr> pub async fn all<'a, C>(self, db: &C) -> Result<Vec<(E::Model, Option<F::Model>)>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -320,6 +340,7 @@ where
self.into_model().all(db).await self.into_model().all(db).await
} }
/// Stream the results of a Select operation on a Model
pub async fn stream<'a: 'b, 'b, C>( pub async fn stream<'a: 'b, 'b, C>(
self, self,
db: &'a C, db: &'a C,
@ -330,6 +351,7 @@ where
self.into_model().stream(db).await self.into_model().stream(db).await
} }
/// Paginate the results of a select operation on two models
pub fn paginate<'a, C>( pub fn paginate<'a, C>(
self, self,
db: &'a C, db: &'a C,
@ -341,6 +363,7 @@ where
self.into_model().paginate(db, page_size) 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<usize, DbErr> pub async fn count<'a, C>(self, db: &'a C) -> Result<usize, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -354,6 +377,7 @@ where
E: EntityTrait, E: EntityTrait,
F: EntityTrait, F: EntityTrait,
{ {
/// Performs a conversion to [Selector]
fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>> fn into_model<M, N>(self) -> Selector<SelectTwoModel<M, N>>
where where
M: FromQueryResult, M: FromQueryResult,
@ -365,6 +389,7 @@ where
} }
} }
/// Convert the results to JSON
#[cfg(feature = "with-json")] #[cfg(feature = "with-json")]
pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> { pub fn into_json(self) -> Selector<SelectTwoModel<JsonValue, JsonValue>> {
Selector { Selector {
@ -373,6 +398,7 @@ where
} }
} }
/// Select one Model
pub async fn one<'a, C>(self, db: &C) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr> pub async fn one<'a, C>(self, db: &C) -> Result<Option<(E::Model, Option<F::Model>)>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -380,6 +406,7 @@ where
self.into_model().one(db).await self.into_model().one(db).await
} }
/// Stream the result of the operation
pub async fn stream<'a: 'b, 'b, C>( pub async fn stream<'a: 'b, 'b, C>(
self, self,
db: &'a C, db: &'a C,
@ -390,6 +417,7 @@ where
self.into_model().stream(db).await self.into_model().stream(db).await
} }
/// Get all the Models from the select operation
pub async fn all<'a, C>(self, db: &C) -> Result<Vec<(E::Model, Vec<F::Model>)>, DbErr> pub async fn all<'a, C>(self, db: &C) -> Result<Vec<(E::Model, Vec<F::Model>)>, DbErr>
where where
C: ConnectionTrait<'a>, 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<Option<S::Item>, DbErr> pub async fn one<'a, C>(mut self, db: &C) -> Result<Option<S::Item>, DbErr>
where where
C: ConnectionTrait<'a>, 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<Vec<S::Item>, DbErr> pub async fn all<'a, C>(self, db: &C) -> Result<Vec<S::Item>, DbErr>
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -454,6 +484,7 @@ where
Ok(models) Ok(models)
} }
/// Stream the results of the operation
pub async fn stream<'a: 'b, 'b, C>( pub async fn stream<'a: 'b, 'b, C>(
self, self,
db: &'a C, 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> pub fn paginate<'a, C>(self, db: &'a C, page_size: usize) -> Paginator<'a, C, S>
where where
C: ConnectionTrait<'a>, 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`. /// return a type `T` which implement `TryGetableMany`.
pub fn with_columns<T, C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<T, C>> pub fn with_columns<T, C>(stmt: Statement) -> SelectorRaw<SelectGetableValue<T, C>>
where where

View File

@ -4,14 +4,17 @@ use crate::{
use sea_query::UpdateStatement; use sea_query::UpdateStatement;
use std::future::Future; use std::future::Future;
/// Defines an update operation
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Updater { pub struct Updater {
query: UpdateStatement, query: UpdateStatement,
check_record_exists: bool, check_record_exists: bool,
} }
/// The result of an update operation on an ActiveModel
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct UpdateResult { pub struct UpdateResult {
/// The rows affected by the update operation
pub rows_affected: u64, pub rows_affected: u64,
} }
@ -19,6 +22,7 @@ impl<'a, A: 'a> UpdateOne<A>
where where
A: ActiveModelTrait, A: ActiveModelTrait,
{ {
/// Execute an update operation on an ActiveModel
pub async fn exec<'b, C>(self, db: &'b C) -> Result<A, DbErr> pub async fn exec<'b, C>(self, db: &'b C) -> Result<A, DbErr>
where where
C: ConnectionTrait<'b>, C: ConnectionTrait<'b>,
@ -32,6 +36,7 @@ impl<'a, E> UpdateMany<E>
where where
E: EntityTrait, E: EntityTrait,
{ {
/// Execute an update operation on multiple ActiveModels
pub fn exec<C>(self, db: &'a C) -> impl Future<Output = Result<UpdateResult, DbErr>> + '_ pub fn exec<C>(self, db: &'a C) -> impl Future<Output = Result<UpdateResult, DbErr>> + '_
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,
@ -42,6 +47,7 @@ where
} }
impl Updater { impl Updater {
/// Instantiate an update using an [UpdateStatement]
pub fn new(query: UpdateStatement) -> Self { pub fn new(query: UpdateStatement) -> Self {
Self { Self {
query, 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 { pub fn check_record_exists(mut self) -> Self {
self.check_record_exists = true; self.check_record_exists = true;
self self
} }
/// Execute an update operation
pub fn exec<'a, C>(self, db: &'a C) -> impl Future<Output = Result<UpdateResult, DbErr>> + '_ pub fn exec<'a, C>(self, db: &'a C) -> impl Future<Output = Result<UpdateResult, DbErr>> + '_
where where
C: ConnectionTrait<'a>, C: ConnectionTrait<'a>,

View File

@ -266,10 +266,15 @@
mod database; mod database;
mod docs; mod docs;
mod driver; mod driver;
/// Module for the Entity type and operations
pub mod entity; pub mod entity;
/// Error types for all database operations
pub mod error; pub mod error;
/// This module performs execution of queries on a Model or ActiveModel
mod executor; mod executor;
/// Holds types and methods to perform queries
pub mod query; pub mod query;
/// Holds types that defines the schemas of an Entity
pub mod schema; pub mod schema;
#[doc(hidden)] #[doc(hidden)]
#[cfg(feature = "macros")] #[cfg(feature = "macros")]

View File

@ -7,6 +7,7 @@ use sea_query::{Alias, ColumnRef, Iden, Order, SeaRc, SelectExpr, SelectStatemen
macro_rules! select_def { macro_rules! select_def {
( $ident: ident, $str: expr ) => { ( $ident: ident, $str: expr ) => {
/// Implements the traits [Iden] and [IdenStatic] for a type
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct $ident; pub struct $ident;
@ -54,6 +55,7 @@ where
self self
} }
/// Selects and Entity and returns it together with the Entity from `Self`
pub fn select_also<F>(mut self, _: F) -> SelectTwo<E, F> pub fn select_also<F>(mut self, _: F) -> SelectTwo<E, F>
where where
F: EntityTrait, F: EntityTrait,
@ -62,6 +64,7 @@ where
SelectTwo::new(self.into_query()) SelectTwo::new(self.into_query())
} }
/// Makes a SELECT operation in conjunction to another relation
pub fn select_with<F>(mut self, _: F) -> SelectTwoMany<E, F> pub fn select_with<F>(mut self, _: F) -> SelectTwoMany<E, F>
where where
F: EntityTrait, F: EntityTrait,

View File

@ -5,9 +5,11 @@ use crate::{
use core::marker::PhantomData; use core::marker::PhantomData;
use sea_query::{DeleteStatement, IntoIden}; use sea_query::{DeleteStatement, IntoIden};
/// Defines the structure for a delete operation
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Delete; pub struct Delete;
/// Perform a delete operation on a model
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DeleteOne<A> pub struct DeleteOne<A>
where where
@ -17,6 +19,7 @@ where
pub(crate) model: A, pub(crate) model: A,
} }
/// Perform a delete operation on multiple models
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct DeleteMany<E> pub struct DeleteMany<E>
where where

View File

@ -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 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 there is a group by clause, but some columns don't have aggregate functions
// LINT: when the join table or column does not exists // 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 { pub trait QuerySelect: Sized {
#[allow(missing_docs)]
type QueryStatement; type QueryStatement;
/// Add the select SQL statement
fn query(&mut self) -> &mut SelectStatement; fn query(&mut self) -> &mut SelectStatement;
/// Clear the selection list /// Clear the selection list
@ -164,9 +167,12 @@ pub trait QuerySelect: Sized {
} }
// LINT: when the column does not appear in tables selected from // LINT: when the column does not appear in tables selected from
/// Performs ORDER BY operations
pub trait QueryOrder: Sized { pub trait QueryOrder: Sized {
#[allow(missing_docs)]
type QueryStatement: OrderedStatement; type QueryStatement: OrderedStatement;
/// Add the query to perform an ORDER BY operation
fn query(&mut self) -> &mut SelectStatement; fn query(&mut self) -> &mut SelectStatement;
/// Add an order_by expression /// Add an order_by expression
@ -234,9 +240,12 @@ pub trait QueryOrder: Sized {
} }
// LINT: when the column does not appear in tables selected from // LINT: when the column does not appear in tables selected from
/// Perform a FILTER opertation on a statement
pub trait QueryFilter: Sized { pub trait QueryFilter: Sized {
#[allow(missing_docs)]
type QueryStatement: ConditionalStatement; type QueryStatement: ConditionalStatement;
/// Add the query to perform a FILTER on
fn query(&mut self) -> &mut Self::QueryStatement; fn query(&mut self) -> &mut Self::QueryStatement;
/// Add an AND WHERE expression /// Add an AND WHERE expression
@ -372,6 +381,7 @@ pub trait QueryFilter: Sized {
self self
} }
/// Perform a check to determine table belongs to a Model through it's name alias
fn belongs_to_tbl_alias<M>(mut self, model: &M, tbl_alias: &str) -> Self fn belongs_to_tbl_alias<M>(mut self, model: &M, tbl_alias: &str) -> Self
where where
M: ModelTrait, M: ModelTrait,

View File

@ -5,6 +5,7 @@ use crate::{
use core::marker::PhantomData; use core::marker::PhantomData;
use sea_query::{Alias, Expr, InsertStatement, ValueTuple}; use sea_query::{Alias, Expr, InsertStatement, ValueTuple};
/// Performs INSERT operations on a ActiveModel
#[derive(Debug)] #[derive(Debug)]
pub struct Insert<A> pub struct Insert<A>
where where
@ -106,6 +107,7 @@ where
Self::new().add_many(models) Self::new().add_many(models)
} }
/// Add a Model to Self
#[allow(clippy::should_implement_trait)] #[allow(clippy::should_implement_trait)]
pub fn add<M>(mut self, m: M) -> Self pub fn add<M>(mut self, m: M) -> Self
where where
@ -144,6 +146,7 @@ where
self self
} }
/// Add many Models to Self
pub fn add_many<M, I>(mut self, models: I) -> Self pub fn add_many<M, I>(mut self, models: I) -> Self
where where
M: IntoActiveModel<A>, M: IntoActiveModel<A>,

View File

@ -4,6 +4,7 @@ use core::marker::PhantomData;
pub use sea_query::JoinType; pub use sea_query::JoinType;
use sea_query::{Alias, DynIden, Expr, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr}; use sea_query::{Alias, DynIden, Expr, IntoColumnRef, SeaRc, SelectStatement, SimpleExpr};
/// Defines a structure to perform select operations
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Select<E> pub struct Select<E>
where where
@ -13,6 +14,7 @@ where
pub(crate) entity: PhantomData<E>, pub(crate) entity: PhantomData<E>,
} }
/// Defines a structure to perform a SELECT operation on two Models
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SelectTwo<E, F> pub struct SelectTwo<E, F>
where where
@ -23,6 +25,7 @@ where
pub(crate) entity: PhantomData<(E, F)>, pub(crate) entity: PhantomData<(E, F)>,
} }
/// Defines a structure to perform a SELECT operation on many Models
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SelectTwoMany<E, F> pub struct SelectTwoMany<E, F>
where where
@ -33,7 +36,9 @@ where
pub(crate) entity: PhantomData<(E, F)>, pub(crate) entity: PhantomData<(E, F)>,
} }
/// Performs a conversion to [SimpleExpr]
pub trait IntoSimpleExpr { pub trait IntoSimpleExpr {
/// Method to perform the conversion
fn into_simple_expr(self) -> SimpleExpr; fn into_simple_expr(self) -> SimpleExpr;
} }

View File

@ -1,7 +1,9 @@
use crate::{DbBackend, Statement}; use crate::{DbBackend, Statement};
use sea_query::QueryStatementBuilder; use sea_query::QueryStatementBuilder;
/// Enforces a set of constraints to any type performing queries on a Model or ActiveModel
pub trait QueryTrait { pub trait QueryTrait {
/// Constrain the QueryStatement to [QueryStatementBuilder] trait
type QueryStatement: QueryStatementBuilder; type QueryStatement: QueryStatementBuilder;
/// Get a mutable ref to the query builder /// Get a mutable ref to the query builder

View File

@ -5,9 +5,11 @@ use crate::{
use core::marker::PhantomData; use core::marker::PhantomData;
use sea_query::{Alias, Expr, IntoIden, SimpleExpr, UpdateStatement}; use sea_query::{Alias, Expr, IntoIden, SimpleExpr, UpdateStatement};
/// Defines a structure to perform UPDATE query operations on a ActiveModel
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Update; pub struct Update;
/// Defines an UPDATE operation on one ActiveModel
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct UpdateOne<A> pub struct UpdateOne<A>
where where
@ -17,6 +19,7 @@ where
pub(crate) model: A, pub(crate) model: A,
} }
/// Defines an UPDATE operation on multiple ActiveModels
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct UpdateMany<E> pub struct UpdateMany<E>
where where
@ -182,6 +185,7 @@ impl<E> UpdateMany<E>
where where
E: EntityTrait, E: EntityTrait,
{ {
/// Add the models to update to Self
pub fn set<A>(mut self, model: A) -> Self pub fn set<A>(mut self, model: A) -> Self
where where
A: ActiveModelTrait<Entity = E>, A: ActiveModelTrait<Entity = E>,
@ -195,6 +199,7 @@ where
self self
} }
/// Creates a [SimpleExpr] from a column
pub fn col_expr<T>(mut self, col: T, expr: SimpleExpr) -> Self pub fn col_expr<T>(mut self, col: T, expr: SimpleExpr) -> Self
where where
T: IntoIden, T: IntoIden,

View File

@ -1,8 +1,11 @@
use crate::{database::*, QueryTrait, Statement}; use crate::{database::*, QueryTrait, Statement};
/// This structure provides debug capabilities
#[derive(Debug)] #[derive(Debug)]
pub struct DebugQuery<'a, Q, T> { pub struct DebugQuery<'a, Q, T> {
/// The query to debug
pub query: &'a Q, pub query: &'a Q,
/// The value of the query
pub value: T, pub value: T,
} }
@ -12,6 +15,7 @@ macro_rules! debug_query_build {
where where
Q: QueryTrait, Q: QueryTrait,
{ {
/// This macro builds a [Statement] when invoked
pub fn build(&self) -> Statement { pub fn build(&self) -> Statement {
let func = $db_expr; let func = $db_expr;
let db_backend = func(self); let db_backend = func(self);

View File

@ -8,6 +8,7 @@ use sea_query::{
}; };
impl Schema { impl Schema {
/// Creates Postgres enums from an Entity. See [TypeCreateStatement] for more details
pub fn create_enum_from_entity<E>(entity: E, db_backend: DbBackend) -> Vec<TypeCreateStatement> pub fn create_enum_from_entity<E>(entity: E, db_backend: DbBackend) -> Vec<TypeCreateStatement>
where where
E: EntityTrait, E: EntityTrait,
@ -15,6 +16,7 @@ impl Schema {
create_enum_from_entity(entity, db_backend) create_enum_from_entity(entity, db_backend)
} }
/// Creates a table from an Entity. See [TableCreateStatement] for more details
pub fn create_table_from_entity<E>(entity: E, db_backend: DbBackend) -> TableCreateStatement pub fn create_table_from_entity<E>(entity: E, db_backend: DbBackend) -> TableCreateStatement
where where
E: EntityTrait, E: EntityTrait,

View File

@ -1,4 +1,5 @@
mod entity; mod entity;
/// This structure defines a schema for a table
#[derive(Debug)] #[derive(Debug)]
pub struct Schema; pub struct Schema;

View File

@ -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] #[macro_export]
#[cfg(feature = "debug-print")] #[cfg(feature = "debug-print")]
macro_rules! debug_print { macro_rules! debug_print {
@ -5,7 +19,7 @@ macro_rules! debug_print {
} }
#[macro_export] #[macro_export]
// Non-debug version /// Non-debug version
#[cfg(not(feature = "debug-print"))] #[cfg(not(feature = "debug-print"))]
macro_rules! debug_print { macro_rules! debug_print {
($( $args:expr ),*) => { ($( $args:expr ),*) => {