From 95de99104d952b6c05672aca3640f538691f832a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alja=C5=BE=20Mur=20Er=C5=BEen?= Date: Thu, 18 Apr 2024 17:21:22 +0200 Subject: [PATCH] Update bigdecimal and add implement Display for Decimal --- edgedb-protocol/Cargo.toml | 2 +- edgedb-protocol/src/model/bignum.rs | 61 +++++++++++++++++++++++++++-- edgedb-tokio/tests/func/client.rs | 38 ++++++++++++++++++ 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/edgedb-protocol/Cargo.toml b/edgedb-protocol/Cargo.toml index 936dbeb0..24a9ee6a 100644 --- a/edgedb-protocol/Cargo.toml +++ b/edgedb-protocol/Cargo.toml @@ -17,7 +17,7 @@ snafu = {version="0.8.0"} uuid = "1.1.2" num-bigint = {version="0.4.3", optional=true} num-traits = {version="0.2.10", optional=true} -bigdecimal = {version="0.3.0", optional=true} +bigdecimal = {version="0.4.0", optional=true} chrono = {version="0.4.23", optional=true, features=["std"], default-features=false} edgedb-errors = {path = "../edgedb-errors", version = "0.4.0" } bitflags = "2.4.0" diff --git a/edgedb-protocol/src/model/bignum.rs b/edgedb-protocol/src/model/bignum.rs index 5b30409f..2c5ca068 100644 --- a/edgedb-protocol/src/model/bignum.rs +++ b/edgedb-protocol/src/model/bignum.rs @@ -1,3 +1,5 @@ +use std::fmt::Write; + #[cfg(feature = "num-bigint")] mod num_bigint_interop; @@ -146,9 +148,58 @@ impl Decimal { } } +impl std::fmt::Display for Decimal { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.negative { + write!(f, "-")?; + } + + let mut index = 0; + + // integer part + while self.weight - index >= 0 { + if let Some(digit) = self.digits.get(index as usize) { + if index == 0 { + write!(f, "{}", digit)?; + } else { + write!(f, "{:04}", digit)?; + } + index += 1; + } else { + break; + } + } + if index == 0 { + write!(f, "0")?; + } + + // dot + write!(f, ".")?; + + // decimal part + let mut decimals = self.decimal_digits; + while decimals > 0 { + if let Some(digit) = self.digits.get(index as usize) { + let digit = format!("{digit:04}"); + let consumed = u16::min(4, decimals); + f.write_str(&digit[0..consumed as usize])?; + decimals -= consumed; + index += 1; + } else { + break; + } + } + // trailing zeros + for _ in 0..decimals { + f.write_char('0')?; + } + Ok(()) + } +} + #[cfg(test)] #[allow(dead_code)] // used by optional tests - mod test_helpers{ +mod test_helpers{ use rand::Rng; pub fn gen_u64(rng: &mut T) -> u64 { @@ -162,6 +213,10 @@ impl Decimal { let max = 10_i64.pow(rng.gen_range(0..19)); rng.gen_range(-max..max) } + + pub fn gen_f64(rng: &mut T) -> f64 { + rng.gen::() + } } #[cfg(test)] @@ -238,7 +293,7 @@ mod test { } #[test] - fn display() { + fn bigint_display() { let cases = [ 0, 1, @@ -255,7 +310,7 @@ mod test { } #[test] - fn display_rand() { + fn bigint_display_rand() { use rand::{Rng, SeedableRng, rngs::StdRng}; let mut rng = StdRng::seed_from_u64(4); for _ in 0..1000 { diff --git a/edgedb-tokio/tests/func/client.rs b/edgedb-tokio/tests/func/client.rs index 532f40ba..7e6a7344 100644 --- a/edgedb-tokio/tests/func/client.rs +++ b/edgedb-tokio/tests/func/client.rs @@ -110,3 +110,41 @@ async fn parallel_queries() -> anyhow::Result<()> { Ok(()) } + +#[tokio::test] +async fn big_num() -> anyhow::Result<()> { + let client = Client::new(&SERVER.config); + client.ensure_connected().await?; + + let res = client + .query_required_single::("select 1234567890123456789012345678900000n", &()) + .await + .unwrap(); + if let Value::BigInt(res) = res { + assert_eq!(res.to_string(), "1234567890123456789012345678900000"); + } else { + panic!(); + } + + let res = client + .query_required_single::("select 1234567891234567890.12345678900000n", &()) + .await + .unwrap(); + if let Value::Decimal(res) = res { + assert_eq!(res.to_string(), "1234567891234567890.12345678900000"); + } else { + panic!(); + } + + let res = client + .query_required_single::("select 0.00012n", &()) + .await + .unwrap(); + if let Value::Decimal(res) = res { + assert_eq!(res.to_string(), "0.00012"); + } else { + panic!(); + } + + Ok(()) +}