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