use rust_decimal::Decimal;
use std::str::FromStr;
#[allow(unused_imports)]
use vantage_sql::sqlite::SqliteType;
use vantage_sql::sqlite::{AnySqliteType, SqliteDB};
use vantage_table::table::Table;
use vantage_types::entity;
use vantage_dataset::{ReadableDataSet, WritableDataSet};
#[entity(SqliteType)]
#[derive(Debug, Clone, PartialEq, Default)]
struct ValDecimal {
name: String,
value: Decimal,
}
macro_rules! table_for {
($table:expr, $db:expr) => {
Table::<SqliteDB, ValDecimal>::new($table, $db)
.with_id_column("id")
.with_column_of::<String>("name")
.with_column_of::<Decimal>("value")
};
}
fn dec(s: &str) -> Decimal {
Decimal::from_str(s).unwrap()
}
async fn setup() -> SqliteDB {
let db = SqliteDB::connect("sqlite::memory:").await.unwrap();
for (name, col_type) in [
("decimal_text", "TEXT"),
("decimal_numeric", "NUMERIC"),
("decimal_real", "REAL"),
("decimal_integer", "INTEGER"),
] {
sqlx::query(&format!(
"CREATE TABLE \"{}\" (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
value {} NOT NULL
)",
name, col_type
))
.execute(db.pool())
.await
.unwrap();
}
db
}
#[tokio::test]
async fn test_text_decimal() {
let db = setup().await;
let t = table_for!("decimal_text", db);
let orig = ValDecimal {
name: "d".into(),
value: dec("123456.789012"),
};
let inserted = t.insert(&"t1".to_string(), &orig).await.unwrap();
assert_eq!(inserted, orig);
let fetched = t.get("t1").await.unwrap().expect("row exists");
assert_eq!(fetched, orig);
}
#[tokio::test]
async fn test_text_high_precision() {
let db = setup().await;
let t = table_for!("decimal_text", db);
let orig = ValDecimal {
name: "hp".into(),
value: dec("99999999999999999.123456789012345"),
};
let inserted = t.insert(&"t2".to_string(), &orig).await.unwrap();
assert_eq!(inserted, orig);
let fetched = t.get("t2").await.unwrap().expect("row exists");
assert_eq!(fetched, orig);
}
#[tokio::test]
async fn test_numeric_decimal() {
let db = setup().await;
let t = table_for!("decimal_numeric", db);
let orig = ValDecimal {
name: "d".into(),
value: dec("123456.789012"),
};
let inserted = t.insert(&"n1".to_string(), &orig).await.unwrap();
assert_eq!(inserted, orig);
let fetched = t.get("n1").await.unwrap().expect("row exists");
assert_eq!(fetched, orig);
}
#[tokio::test]
async fn test_numeric_high_precision_lossy() {
let db = setup().await;
let t = table_for!("decimal_numeric", db);
let orig = ValDecimal {
name: "hp".into(),
value: dec("99999999999999999.123456789012345"),
};
let inserted = t.insert(&"n2".to_string(), &orig).await.unwrap();
assert_ne!(inserted, orig);
}
#[tokio::test]
async fn test_real_simple() {
let db = setup().await;
let t = table_for!("decimal_real", db);
let orig = ValDecimal {
name: "d".into(),
value: dec("123.456"),
};
let inserted = t.insert(&"r1".to_string(), &orig).await.unwrap();
let diff = (inserted.value - orig.value).abs();
assert!(diff < dec("0.001"), "diff too large: {}", diff);
}
#[tokio::test]
async fn test_integer_whole() {
let db = setup().await;
let t = table_for!("decimal_integer", db);
let orig = ValDecimal {
name: "d".into(),
value: dec("42"),
};
let inserted = t.insert(&"i1".to_string(), &orig).await.unwrap();
assert_eq!(inserted.value, dec("42"));
}
#[tokio::test]
async fn test_integer_truncates() {
let db = setup().await;
let t = table_for!("decimal_integer", db);
let orig = ValDecimal {
name: "frac".into(),
value: dec("123.999"),
};
let inserted = t.insert(&"i2".to_string(), &orig).await.unwrap();
let v = inserted.value;
assert!(v == dec("123.999") || v == dec("124"), "unexpected: {}", v);
}