reifydb_type/value/decimal/
parse.rs1use std::{borrow::Cow, str::FromStr};
5
6use bigdecimal::BigDecimal as BigDecimalInner;
7
8use crate::{
9 error::{Error, TypeError},
10 fragment::Fragment,
11 value::{decimal::Decimal, r#type::Type},
12};
13
14pub fn parse_decimal(fragment: Fragment) -> Result<Decimal, Error> {
15 let fragment_owned = fragment.clone();
17 let raw_value = fragment.text();
18
19 let needs_trimming = raw_value.as_bytes().first().map_or(false, |&b| b.is_ascii_whitespace())
21 || raw_value.as_bytes().last().map_or(false, |&b| b.is_ascii_whitespace());
22 let has_underscores = raw_value.as_bytes().contains(&b'_');
23
24 let value = match (needs_trimming, has_underscores) {
25 (false, false) => Cow::Borrowed(raw_value),
26 (true, false) => Cow::Borrowed(raw_value.trim()),
27 (false, true) => Cow::Owned(raw_value.replace('_', "")),
28 (true, true) => Cow::Owned(raw_value.trim().replace('_', "")),
29 };
30
31 if value.is_empty() {
32 return Err(TypeError::InvalidNumberFormat {
33 target: Type::Decimal,
34 fragment: fragment_owned,
35 }
36 .into());
37 }
38
39 let big_decimal = BigDecimalInner::from_str(&value).map_err(|_| -> Error {
40 TypeError::InvalidNumberFormat {
41 target: Type::Decimal,
42 fragment: fragment_owned,
43 }
44 .into()
45 })?;
46
47 Ok(Decimal::new(big_decimal))
48}
49
50#[cfg(test)]
51pub mod tests {
52 use super::*;
53
54 #[test]
55 fn test_parse_decimal_integer() {
56 let decimal = parse_decimal(Fragment::testing("123")).unwrap();
57 assert_eq!(decimal.to_string(), "123");
58 }
59
60 #[test]
61 fn test_parse_decimal_with_fractional() {
62 let decimal = parse_decimal(Fragment::testing("123.45")).unwrap();
63 assert_eq!(decimal.to_string(), "123.45");
64 }
65
66 #[test]
67 fn test_parse_decimal_with_underscores() {
68 let decimal = parse_decimal(Fragment::testing("1_234.56")).unwrap();
69 assert_eq!(decimal.to_string(), "1234.56");
70 }
71
72 #[test]
73 fn test_parse_decimal_negative() {
74 let decimal = parse_decimal(Fragment::testing("-123.45")).unwrap();
75 assert_eq!(decimal.to_string(), "-123.45");
76 }
77
78 #[test]
79 fn test_parse_decimal_empty() {
80 assert!(parse_decimal(Fragment::testing("")).is_err());
81 }
82
83 #[test]
84 fn test_parse_decimal_invalid() {
85 assert!(parse_decimal(Fragment::testing("not_a_number")).is_err());
86 }
87
88 #[test]
89 fn test_parse_decimal_scientific_notation() {
90 let decimal = parse_decimal(Fragment::testing("1.23e2")).unwrap();
91 assert_eq!(decimal.to_string(), "123");
92 }
93}