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