diff --git a/Cargo.toml b/Cargo.toml index 2773730f..0243a2f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,18 +12,24 @@ members = [ [package] name = "sea-orm" version = "0.1.0" -authors = [ "Chris Tsang " ] +authors = ["Chris Tsang "] edition = "2018" description = "🐚 An async & dynamic ORM for Rust" license = "MIT OR Apache-2.0" documentation = "https://docs.rs/sea-orm" repository = "https://github.com/SeaQL/sea-orm" -categories = [ "database" ] -keywords = [ "orm", "database", "sql", "mysql", "postgres", "sqlite", "async" ] +categories = ["database"] +keywords = ["orm", "database", "sql", "mysql", "postgres", "sqlite", "async"] publish = false [package.metadata.docs.rs] -features = ["default", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", "runtime-async-std-native-tls"] +features = [ + "default", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "runtime-async-std-native-tls", +] rustdoc-args = ["--cfg", "docsrs"] [lib] @@ -31,38 +37,52 @@ name = "sea_orm" path = "src/lib.rs" [dependencies] -async-stream = { version = "^0.3" } -chrono = { version = "^0", optional = true } -futures = { version = "^0.3" } -futures-util = { version = "^0.3" } -sea-query = { version = "^0.12" } -sea-orm-macros = { path = "sea-orm-macros", optional = true } -serde = { version = "^1.0", features = [ "derive" ] } -sqlx = { version = "^0.5", optional = true } -strum = { git = "https://github.com/SeaQL/strum.git", branch = "sea-orm", version = "^0.21", features = [ "derive", "sea-orm" ] } -serde_json = { version = "^1", optional = true } +async-stream = { version="^0.3" } +chrono = { version="^0", optional=true } +futures = { version="^0.3" } +futures-util = { version="^0.3" } +rust_decimal = "1.14" +rust_decimal_macros = "1.14" + +sea-query = { version="^0.12.5" } +# sea-query = { path="../sea-query", features=[ +# "with-rust_decimal", +# "postgres-rust_decimal", +# "sqlx-mysql", +# ] } + +sea-orm-macros = { path="sea-orm-macros", optional=true } +serde = { version="^1.0", features=["derive"] } +sqlx = { version="^0.5", optional=true } +strum = { git="https://github.com/SeaQL/strum.git", branch="sea-orm", version="^0.21", features=["derive", "sea-orm"] } +serde_json = { version="^1", optional=true } [dev-dependencies] -async-std = { version = "^1.9", features = [ "attributes" ] } -maplit = { version = "^1" } -sea-orm = { path = ".", features = ["sqlx-sqlite", "sqlx-json", "sqlx-chrono", "runtime-async-std-native-tls"] } +async-std = { version="^1.9", features=["attributes"] } +maplit = { version="^1" } +sea-orm = { path=".", features=[ + "sqlx-sqlite", + "sqlx-json", + "sqlx-chrono", + "runtime-async-std-native-tls", +] } [features] debug-print = [] -default = [ "macros", "with-json", "with-chrono", "mock" ] -macros = [ "sea-orm-macros" ] +default = ["macros", "with-json", "with-chrono", "mock"] +macros = ["sea-orm-macros"] mock = [] -with-json = [ "serde_json", "sea-query/with-json" ] -with-chrono = [ "chrono", "sea-query/with-chrono" ] -sqlx-dep = [ "sqlx" ] -sqlx-json = [ "sqlx/json", "with-json" ] -sqlx-chrono = [ "sqlx/chrono", "with-chrono" ] -sqlx-mysql = [ "sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql" ] -sqlx-postgres = [ "sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres" ] -sqlx-sqlite = [ "sqlx-dep", "sea-query/sqlx-sqlite", "sqlx/sqlite" ] -runtime-actix-native-tls = [ "sqlx/runtime-actix-native-tls" ] -runtime-async-std-native-tls = [ "sqlx/runtime-async-std-native-tls" ] -runtime-tokio-native-tls = [ "sqlx/runtime-tokio-native-tls" ] -runtime-actix-rustls = [ "sqlx/runtime-actix-rustls" ] -runtime-async-std-rustls = [ "sqlx/runtime-async-std-rustls" ] -runtime-tokio-rustls = [ "sqlx/runtime-tokio-rustls" ] \ No newline at end of file +with-json = ["serde_json", "sea-query/with-json"] +with-chrono = ["chrono", "sea-query/with-chrono"] +sqlx-dep = ["sqlx"] +sqlx-json = ["sqlx/json", "with-json"] +sqlx-chrono = ["sqlx/chrono", "with-chrono"] +sqlx-mysql = ["sqlx-dep", "sea-query/sqlx-mysql", "sqlx/mysql"] +sqlx-postgres = ["sqlx-dep", "sea-query/sqlx-postgres", "sqlx/postgres"] +sqlx-sqlite = ["sqlx-dep", "sea-query/sqlx-sqlite", "sqlx/sqlite"] +runtime-actix-native-tls = ["sqlx/runtime-actix-native-tls"] +runtime-async-std-native-tls = ["sqlx/runtime-async-std-native-tls"] +runtime-tokio-native-tls = ["sqlx/runtime-tokio-native-tls"] +runtime-actix-rustls = ["sqlx/runtime-actix-rustls"] +runtime-async-std-rustls = ["sqlx/runtime-async-std-rustls"] +runtime-tokio-rustls = ["sqlx/runtime-tokio-rustls"] diff --git a/src/executor/query.rs b/src/executor/query.rs index 1d23a013..ef43318a 100644 --- a/src/executor/query.rs +++ b/src/executor/query.rs @@ -1,4 +1,5 @@ use crate::DbErr; +use rust_decimal::prelude::*; use std::fmt; #[derive(Debug)] @@ -46,6 +47,25 @@ impl fmt::Debug for QueryResultRow { } // TryGetable // +impl TryGetable for Decimal { + fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result { + let column = format!("{}{}", pre, col); + match &res.row { + #[cfg(feature = "sqlx-mysql")] + QueryResultRow::SqlxMySql(row) => { + use sqlx::Row; + row.try_get(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) + } + #[cfg(feature = "sqlx-sqlite")] + QueryResultRow::SqlxSqlite(_) => { + panic!("{} unsupported by sqlx-sqlite", stringify!($type)) + } + #[cfg(feature = "mock")] + QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?), + } + } +} macro_rules! try_getable_all { ( $type: ty ) => { @@ -56,12 +76,14 @@ macro_rules! try_getable_all { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err) + row.try_get(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(row) => { use sqlx::Row; - row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err) + row.try_get(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) } #[cfg(feature = "mock")] QueryResultRow::Mock(row) => Ok(row.try_get(column.as_str())?), @@ -109,7 +131,8 @@ macro_rules! try_getable_mysql { #[cfg(feature = "sqlx-mysql")] QueryResultRow::SqlxMySql(row) => { use sqlx::Row; - row.try_get(column.as_str()).map_err(crate::sqlx_error_to_query_err) + row.try_get(column.as_str()) + .map_err(crate::sqlx_error_to_query_err) } #[cfg(feature = "sqlx-sqlite")] QueryResultRow::SqlxSqlite(_) => { diff --git a/tests/bakery_chain/lineitem.rs b/tests/bakery_chain/lineitem.rs index f4c9e3b6..94b15cac 100644 --- a/tests/bakery_chain/lineitem.rs +++ b/tests/bakery_chain/lineitem.rs @@ -1,3 +1,4 @@ +use rust_decimal::prelude::*; use sea_orm::entity::prelude::*; #[derive(Copy, Clone, Default, Debug, DeriveEntity)] @@ -12,7 +13,7 @@ impl EntityName for Entity { #[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel)] pub struct Model { pub id: i32, - pub price: f64, + pub price: Decimal, pub quantity: i32, pub order_id: Option, pub cake_id: Option, diff --git a/tests/bakery_chain_tests.rs b/tests/bakery_chain_tests.rs index a51848e5..03effc64 100644 --- a/tests/bakery_chain_tests.rs +++ b/tests/bakery_chain_tests.rs @@ -29,4 +29,5 @@ async fn create_entities(db: &DbConn) { crud::test_create_baker(db).await; crud::test_create_customer(db).await; crud::test_create_cake(db).await; + crud::create_lineitem::test_create_lineitem(db).await; } diff --git a/tests/crud/create_lineitem.rs b/tests/crud/create_lineitem.rs new file mode 100644 index 00000000..d5857ee6 --- /dev/null +++ b/tests/crud/create_lineitem.rs @@ -0,0 +1,96 @@ +pub use super::*; +use rust_decimal_macros::dec; + +pub async fn test_create_lineitem(db: &DbConn) { + // Bakery + let seaside_bakery = bakery::ActiveModel { + name: Set("SeaSide Bakery".to_owned()), + profit_margin: Set(10.4), + ..Default::default() + }; + let bakery_insert_res: InsertResult = Bakery::insert(seaside_bakery) + .exec(db) + .await + .expect("could not insert bakery"); + + // Baker + let baker_bob = baker::ActiveModel { + name: Set("Baker Bob".to_owned()), + bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), + ..Default::default() + }; + let baker_insert_res: InsertResult = Baker::insert(baker_bob) + .exec(db) + .await + .expect("could not insert baker"); + + // Cake + let mud_cake = cake::ActiveModel { + name: Set("Mud Cake".to_owned()), + price: Set(10.25), + gluten_free: Set(false), + bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), + ..Default::default() + }; + + let cake_insert_res: InsertResult = Cake::insert(mud_cake) + .exec(db) + .await + .expect("could not insert cake"); + + // Cake_Baker + let cake_baker = cakes_bakers::ActiveModel { + cake_id: Set(cake_insert_res.last_insert_id as i32), + baker_id: Set(baker_insert_res.last_insert_id as i32), + ..Default::default() + }; + let _cake_baker_res: InsertResult = CakesBakers::insert(cake_baker) + .exec(db) + .await + .expect("could not insert cake_baker"); + + // Customer + let customer_kate = customer::ActiveModel { + name: Set("Kate".to_owned()), + notes: Set("Loves cheese cake".to_owned()), + ..Default::default() + }; + let customer_insert_res: InsertResult = Customer::insert(customer_kate) + .exec(db) + .await + .expect("could not insert customer"); + + // Order + let order_1 = order::ActiveModel { + bakery_id: Set(Some(bakery_insert_res.last_insert_id as i32)), + customer_id: Set(Some(customer_insert_res.last_insert_id as i32)), + placed_at: Set("placeholder".to_string()), + ..Default::default() + }; + let order_insert_res: InsertResult = Order::insert(order_1) + .exec(db) + .await + .expect("could not insert order"); + + // Lineitem + let lineitem_1 = lineitem::ActiveModel { + cake_id: Set(Some(cake_insert_res.last_insert_id as i32)), + order_id: Set(Some(order_insert_res.last_insert_id as i32)), + price: Set(dec!(7.55)), + ..Default::default() + }; + let lineitem_insert_res: InsertResult = Lineitem::insert(lineitem_1) + .exec(db) + .await + .expect("could not insert lineitem"); + + let lineitem: Option = + Lineitem::find_by_id(lineitem_insert_res.last_insert_id) + .one(db) + .await + .expect("could not find lineitem"); + + assert!(lineitem.is_some()); + let lineitem_model = lineitem.unwrap(); + // assert_eq!(lineitem_model.price, dec!(7.55)); +} diff --git a/tests/crud/mod.rs b/tests/crud/mod.rs index e058993b..01ec4122 100644 --- a/tests/crud/mod.rs +++ b/tests/crud/mod.rs @@ -2,6 +2,8 @@ use sea_orm::{entity::*, DbConn, InsertResult}; pub use super::bakery_chain::*; +pub mod create_lineitem; + pub async fn test_create_bakery(db: &DbConn) { let seaside_bakery = bakery::ActiveModel { name: Set("SeaSide Bakery".to_owned()), diff --git a/tests/schema/mod.rs b/tests/schema/mod.rs index 29db082c..6ebcc381 100644 --- a/tests/schema/mod.rs +++ b/tests/schema/mod.rs @@ -125,7 +125,7 @@ pub async fn create_lineitem_table(db: &DbConn) -> Result { .auto_increment() .primary_key(), ) - .col(ColumnDef::new(lineitem::Column::Price).float()) + .col(ColumnDef::new(lineitem::Column::Price).decimal()) .col(ColumnDef::new(lineitem::Column::Quantity).integer()) .col( ColumnDef::new(lineitem::Column::OrderId)