reifydb_type/value/uint/
parse.rs1use std::borrow::Cow;
5
6use num_bigint::{BigInt, Sign};
7
8use crate::{
9 error::{Error, TypeError},
10 fragment::Fragment,
11 value::{r#type::Type, uint::Uint},
12};
13
14pub fn parse_uint(fragment: Fragment) -> Result<Uint, Error> {
15 let raw_value = fragment.text();
17
18 let needs_trimming = raw_value.as_bytes().first().map_or(false, |&b| b.is_ascii_whitespace())
20 || raw_value.as_bytes().last().map_or(false, |&b| b.is_ascii_whitespace());
21
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), (true, false) => Cow::Borrowed(raw_value.trim()),
29 (false, true) => Cow::Owned(raw_value.replace('_', "")),
30 (true, true) => Cow::Owned(raw_value.trim().replace('_', "")),
31 };
32
33 if value.is_empty() {
34 return Err(TypeError::InvalidNumberFormat {
35 target: Type::Uint,
36 fragment,
37 }
38 .into());
39 }
40
41 if value.starts_with('-') && value != "-0.0" && value != "-0" {
44 if let Ok(bigint) = value.parse::<BigInt>() {
46 if bigint.sign() == Sign::Minus {
47 return Err(TypeError::NumberOutOfRange {
48 target: Type::Uint,
49 fragment,
50 descriptor: None,
51 }
52 .into());
53 }
54 }
55 }
57
58 match value.parse::<BigInt>() {
60 Ok(v) => {
61 if v.sign() == Sign::Minus {
64 return Err(TypeError::NumberOutOfRange {
65 target: Type::Uint,
66 fragment,
67 descriptor: None,
68 }
69 .into());
70 }
71 Ok(Uint::from(v))
72 }
73 Err(_) => {
74 if let Ok(f) = value.parse::<f64>() {
77 if f.is_infinite() {
78 Err(TypeError::NumberOutOfRange {
79 target: Type::Uint,
80 fragment,
81 descriptor: None,
82 }
83 .into())
84 } else {
85 let truncated = f.trunc();
86 if truncated < 0.0 && truncated != -0.0 {
89 return Err(TypeError::NumberOutOfRange {
90 target: Type::Uint,
91 fragment,
92 descriptor: None,
93 }
94 .into());
95 }
96 let abs_truncated = if truncated == -0.0 {
99 0.0
100 } else {
101 truncated
102 };
103 if let Ok(bigint) = format!("{:.0}", abs_truncated).parse::<BigInt>() {
104 Ok(Uint::from(bigint))
105 } else {
106 Err(TypeError::InvalidNumberFormat {
107 target: Type::Uint,
108 fragment,
109 }
110 .into())
111 }
112 }
113 } else {
114 if value.contains('-') {
117 Err(TypeError::NumberOutOfRange {
118 target: Type::Uint,
119 fragment,
120 descriptor: None,
121 }
122 .into())
123 } else {
124 Err(TypeError::InvalidNumberFormat {
125 target: Type::Uint,
126 fragment,
127 }
128 .into())
129 }
130 }
131 }
132 }
133}
134
135#[cfg(test)]
136pub mod tests {
137 use super::*;
138
139 #[test]
140 fn test_parse_uint_valid_zero() {
141 assert_eq!(parse_uint(Fragment::testing("0")).unwrap(), Uint::zero());
142 }
143
144 #[test]
145 fn test_parse_uint_valid_positive() {
146 let result = parse_uint(Fragment::testing("12345")).unwrap();
147 assert_eq!(format!("{}", result), "12345");
148 }
149
150 #[test]
151 fn test_parse_uint_large_positive() {
152 let large_num = "123456789012345678901234567890";
153 let result = parse_uint(Fragment::testing(large_num)).unwrap();
154 assert_eq!(format!("{}", result), large_num);
155 }
156
157 #[test]
158 fn test_parse_uint_scientific_notation() {
159 let result = parse_uint(Fragment::testing("1e5")).unwrap();
160 assert_eq!(format!("{}", result), "100000");
161 }
162
163 #[test]
164 fn test_parse_uint_scientific_decimal() {
165 let result = parse_uint(Fragment::testing("2.5e3")).unwrap();
166 assert_eq!(format!("{}", result), "2500");
167 }
168
169 #[test]
170 fn test_parse_uint_float_truncation() {
171 let result = parse_uint(Fragment::testing("123.789")).unwrap();
172 assert_eq!(format!("{}", result), "123");
173 }
174
175 #[test]
176 fn test_parse_uint_float_truncation_zero() {
177 let result = parse_uint(Fragment::testing("0.999")).unwrap();
178 assert_eq!(format!("{}", result), "0");
179 }
180
181 #[test]
182 fn test_parse_uint_with_underscores() {
183 let result = parse_uint(Fragment::testing("1_234_567")).unwrap();
184 assert_eq!(format!("{}", result), "1234567");
185 }
186
187 #[test]
188 fn test_parse_uint_with_leading_space() {
189 let result = parse_uint(Fragment::testing(" 12345")).unwrap();
190 assert_eq!(format!("{}", result), "12345");
191 }
192
193 #[test]
194 fn test_parse_uint_with_trailing_space() {
195 let result = parse_uint(Fragment::testing("12345 ")).unwrap();
196 assert_eq!(format!("{}", result), "12345");
197 }
198
199 #[test]
200 fn test_parse_uint_with_both_spaces() {
201 let result = parse_uint(Fragment::testing(" 12345 ")).unwrap();
202 assert_eq!(format!("{}", result), "12345");
203 }
204
205 #[test]
206 fn test_parse_uint_negative_integer() {
207 assert!(parse_uint(Fragment::testing("-12345")).is_err());
208 }
209
210 #[test]
211 fn test_parse_uint_negative_float() {
212 assert!(parse_uint(Fragment::testing("-123.45")).is_err());
213 }
214
215 #[test]
216 fn test_parse_uint_negative_scientific() {
217 assert!(parse_uint(Fragment::testing("-1e5")).is_err());
218 }
219
220 #[test]
221 fn test_parse_uint_negative_zero_float() {
222 let result = parse_uint(Fragment::testing("-0.0")).unwrap();
225 assert_eq!(format!("{}", result), "0");
226 }
227
228 #[test]
229 fn test_parse_uint_invalid_empty() {
230 assert!(parse_uint(Fragment::testing("")).is_err());
231 }
232
233 #[test]
234 fn test_parse_uint_invalid_whitespace() {
235 assert!(parse_uint(Fragment::testing(" ")).is_err());
236 }
237
238 #[test]
239 fn test_parse_uint_invalid_text() {
240 assert!(parse_uint(Fragment::testing("abc")).is_err());
241 }
242
243 #[test]
244 fn test_parse_uint_invalid_multiple_dots() {
245 assert!(parse_uint(Fragment::testing("1.2.3")).is_err());
246 }
247
248 #[test]
249 fn test_parse_uint_infinity() {
250 assert!(parse_uint(Fragment::testing("inf")).is_err());
251 }
252
253 #[test]
254 fn test_parse_uint_negative_infinity() {
255 assert!(parse_uint(Fragment::testing("-inf")).is_err());
256 }
257}