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