reifydb_type/value/uint/
parse.rs1use std::borrow::Cow;
5
6use num_bigint::BigInt;
7
8use crate::{
9 Error, IntoFragment, Type, err,
10 error::diagnostic::number::{invalid_number_format, number_out_of_range},
11 return_error,
12 value::uint::Uint,
13};
14
15pub fn parse_uint<'a>(fragment: impl IntoFragment<'a>) -> Result<Uint, Error> {
16 let fragment = fragment.into_fragment();
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
23 let has_underscores = raw_value.as_bytes().contains(&b'_');
24
25 let value = match (needs_trimming, has_underscores) {
26 (false, false) => Cow::Borrowed(raw_value), (true, false) => Cow::Borrowed(raw_value.trim()),
30 (false, true) => Cow::Owned(raw_value.replace('_', "")),
31 (true, true) => Cow::Owned(raw_value.trim().replace('_', "")),
32 };
33
34 if value.is_empty() {
35 return_error!(invalid_number_format(fragment, Type::Uint));
36 }
37
38 if value.starts_with('-') && value != "-0.0" && value != "-0" {
41 if let Ok(bigint) = value.parse::<BigInt>() {
43 if bigint.sign() == num_bigint::Sign::Minus {
44 return_error!(number_out_of_range(fragment, Type::Uint, None));
45 }
46 }
47 }
49
50 match value.parse::<BigInt>() {
52 Ok(v) => {
53 if v.sign() == num_bigint::Sign::Minus {
56 return_error!(number_out_of_range(fragment, Type::Uint, None));
57 }
58 Ok(Uint::from(v))
59 }
60 Err(_) => {
61 if let Ok(f) = value.parse::<f64>() {
64 if f.is_infinite() {
65 err!(number_out_of_range(fragment, Type::Uint, None))
66 } else {
67 let truncated = f.trunc();
68 if truncated < 0.0 && truncated != -0.0 {
71 return_error!(number_out_of_range(fragment, Type::Uint, None));
72 }
73 let abs_truncated = if truncated == -0.0 {
76 0.0
77 } else {
78 truncated
79 };
80 if let Ok(bigint) = format!("{:.0}", abs_truncated).parse::<BigInt>() {
81 Ok(Uint::from(bigint))
82 } else {
83 err!(invalid_number_format(fragment, Type::Uint))
84 }
85 }
86 } else {
87 if value.contains('-') {
90 err!(number_out_of_range(fragment, Type::Uint, None))
91 } else {
92 err!(invalid_number_format(fragment, Type::Uint))
93 }
94 }
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::OwnedFragment;
103
104 #[test]
105 fn test_parse_uint_valid_zero() {
106 assert_eq!(parse_uint(OwnedFragment::testing("0")).unwrap(), Uint::zero());
107 }
108
109 #[test]
110 fn test_parse_uint_valid_positive() {
111 let result = parse_uint(OwnedFragment::testing("12345")).unwrap();
112 assert_eq!(format!("{}", result), "12345");
113 }
114
115 #[test]
116 fn test_parse_uint_large_positive() {
117 let large_num = "123456789012345678901234567890";
118 let result = parse_uint(OwnedFragment::testing(large_num)).unwrap();
119 assert_eq!(format!("{}", result), large_num);
120 }
121
122 #[test]
123 fn test_parse_uint_scientific_notation() {
124 let result = parse_uint(OwnedFragment::testing("1e5")).unwrap();
125 assert_eq!(format!("{}", result), "100000");
126 }
127
128 #[test]
129 fn test_parse_uint_scientific_decimal() {
130 let result = parse_uint(OwnedFragment::testing("2.5e3")).unwrap();
131 assert_eq!(format!("{}", result), "2500");
132 }
133
134 #[test]
135 fn test_parse_uint_float_truncation() {
136 let result = parse_uint(OwnedFragment::testing("123.789")).unwrap();
137 assert_eq!(format!("{}", result), "123");
138 }
139
140 #[test]
141 fn test_parse_uint_float_truncation_zero() {
142 let result = parse_uint(OwnedFragment::testing("0.999")).unwrap();
143 assert_eq!(format!("{}", result), "0");
144 }
145
146 #[test]
147 fn test_parse_uint_with_underscores() {
148 let result = parse_uint(OwnedFragment::testing("1_234_567")).unwrap();
149 assert_eq!(format!("{}", result), "1234567");
150 }
151
152 #[test]
153 fn test_parse_uint_with_leading_space() {
154 let result = parse_uint(OwnedFragment::testing(" 12345")).unwrap();
155 assert_eq!(format!("{}", result), "12345");
156 }
157
158 #[test]
159 fn test_parse_uint_with_trailing_space() {
160 let result = parse_uint(OwnedFragment::testing("12345 ")).unwrap();
161 assert_eq!(format!("{}", result), "12345");
162 }
163
164 #[test]
165 fn test_parse_uint_with_both_spaces() {
166 let result = parse_uint(OwnedFragment::testing(" 12345 ")).unwrap();
167 assert_eq!(format!("{}", result), "12345");
168 }
169
170 #[test]
171 fn test_parse_uint_negative_integer() {
172 assert!(parse_uint(OwnedFragment::testing("-12345")).is_err());
173 }
174
175 #[test]
176 fn test_parse_uint_negative_float() {
177 assert!(parse_uint(OwnedFragment::testing("-123.45")).is_err());
178 }
179
180 #[test]
181 fn test_parse_uint_negative_scientific() {
182 assert!(parse_uint(OwnedFragment::testing("-1e5")).is_err());
183 }
184
185 #[test]
186 fn test_parse_uint_negative_zero_float() {
187 let result = parse_uint(OwnedFragment::testing("-0.0")).unwrap();
190 assert_eq!(format!("{}", result), "0");
191 }
192
193 #[test]
194 fn test_parse_uint_invalid_empty() {
195 assert!(parse_uint(OwnedFragment::testing("")).is_err());
196 }
197
198 #[test]
199 fn test_parse_uint_invalid_whitespace() {
200 assert!(parse_uint(OwnedFragment::testing(" ")).is_err());
201 }
202
203 #[test]
204 fn test_parse_uint_invalid_text() {
205 assert!(parse_uint(OwnedFragment::testing("abc")).is_err());
206 }
207
208 #[test]
209 fn test_parse_uint_invalid_multiple_dots() {
210 assert!(parse_uint(OwnedFragment::testing("1.2.3")).is_err());
211 }
212
213 #[test]
214 fn test_parse_uint_infinity() {
215 assert!(parse_uint(OwnedFragment::testing("inf")).is_err());
216 }
217
218 #[test]
219 fn test_parse_uint_negative_infinity() {
220 assert!(parse_uint(OwnedFragment::testing("-inf")).is_err());
221 }
222}