reifydb_type/value/decimal/
parse.rs1use std::{borrow::Cow, str::FromStr};
5
6use bigdecimal::BigDecimal as BigDecimalInner;
7
8use crate::{
9 Error, Fragment, Type, error::diagnostic::number::invalid_number_format, return_error, value::decimal::Decimal,
10};
11
12pub fn parse_decimal(fragment: Fragment) -> Result<Decimal, Error> {
13 let fragment_owned = fragment.clone();
15 let raw_value = fragment.text();
16
17 let needs_trimming = raw_value.as_bytes().first().map_or(false, |&b| b.is_ascii_whitespace())
19 || raw_value.as_bytes().last().map_or(false, |&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_error!(invalid_number_format(fragment_owned.clone(), Type::Decimal));
31 }
32
33 let big_decimal = BigDecimalInner::from_str(&value)
34 .map_err(|_| crate::error!(invalid_number_format(fragment_owned, Type::Decimal)))?;
35
36 Ok(Decimal::new(big_decimal))
37}
38
39#[cfg(test)]
40mod tests {
41 use super::*;
42 use crate::Fragment;
43
44 #[test]
45 fn test_parse_decimal_integer() {
46 let decimal = parse_decimal(Fragment::testing("123")).unwrap();
47 assert_eq!(decimal.to_string(), "123");
48 }
49
50 #[test]
51 fn test_parse_decimal_with_fractional() {
52 let decimal = parse_decimal(Fragment::testing("123.45")).unwrap();
53 assert_eq!(decimal.to_string(), "123.45");
54 }
55
56 #[test]
57 fn test_parse_decimal_with_underscores() {
58 let decimal = parse_decimal(Fragment::testing("1_234.56")).unwrap();
59 assert_eq!(decimal.to_string(), "1234.56");
60 }
61
62 #[test]
63 fn test_parse_decimal_negative() {
64 let decimal = parse_decimal(Fragment::testing("-123.45")).unwrap();
65 assert_eq!(decimal.to_string(), "-123.45");
66 }
67
68 #[test]
69 fn test_parse_decimal_empty() {
70 assert!(parse_decimal(Fragment::testing("")).is_err());
71 }
72
73 #[test]
74 fn test_parse_decimal_invalid() {
75 assert!(parse_decimal(Fragment::testing("not_a_number")).is_err());
76 }
77
78 #[test]
79 fn test_parse_decimal_scientific_notation() {
80 let decimal = parse_decimal(Fragment::testing("1.23e2")).unwrap();
81 assert_eq!(decimal.to_string(), "123");
82 }
83}