reifydb_value/value/int/
parse.rs1use std::borrow::Cow;
5
6use num_bigint::BigInt;
7
8use crate::{
9 error::{Error, TypeError},
10 fragment::Fragment,
11 value::{int::Int, value_type::ValueType},
12};
13
14pub fn parse_int(fragment: Fragment) -> Result<Int, 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 let has_underscores = raw_value.as_bytes().contains(&b'_');
20
21 let value = match (needs_trimming, has_underscores) {
22 (false, false) => Cow::Borrowed(raw_value),
23
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 Err(TypeError::InvalidNumberFormat {
31 target: ValueType::Int,
32 fragment,
33 }
34 .into());
35 }
36
37 match value.parse::<BigInt>() {
38 Ok(v) => Ok(Int::from(v)),
39 Err(_) => {
40 if let Ok(f) = value.parse::<f64>() {
41 if f.is_infinite() {
42 Err(TypeError::NumberOutOfRange {
43 target: ValueType::Int,
44 fragment,
45 descriptor: None,
46 }
47 .into())
48 } else {
49 let truncated = f.trunc();
50
51 if let Ok(bigint) = format!("{:.0}", truncated).parse::<BigInt>() {
52 Ok(Int::from(bigint))
53 } else {
54 Err(TypeError::InvalidNumberFormat {
55 target: ValueType::Int,
56 fragment,
57 }
58 .into())
59 }
60 }
61 } else {
62 Err(TypeError::InvalidNumberFormat {
63 target: ValueType::Int,
64 fragment,
65 }
66 .into())
67 }
68 }
69 }
70}
71
72#[cfg(test)]
73pub mod tests {
74 use super::*;
75
76 #[test]
77 fn test_parse_int_valid_zero() {
78 assert_eq!(parse_int(Fragment::testing("0")).unwrap(), Int::zero());
79 }
80
81 #[test]
82 fn test_parse_int_valid_positive() {
83 let result = parse_int(Fragment::testing("12345")).unwrap();
84 assert_eq!(format!("{}", result), "12345");
85 }
86
87 #[test]
88 fn test_parse_int_valid_negative() {
89 let result = parse_int(Fragment::testing("-12345")).unwrap();
90 assert_eq!(format!("{}", result), "-12345");
91 }
92
93 #[test]
94 fn test_parse_int_large_positive() {
95 let large_num = "123456789012345678901234567890";
96 let result = parse_int(Fragment::testing(large_num)).unwrap();
97 assert_eq!(format!("{}", result), large_num);
98 }
99
100 #[test]
101 fn test_parse_int_large_negative() {
102 let large_num = "-123456789012345678901234567890";
103 let result = parse_int(Fragment::testing(large_num)).unwrap();
104 assert_eq!(format!("{}", result), large_num);
105 }
106
107 #[test]
108 fn test_parse_int_scientific_notation() {
109 let result = parse_int(Fragment::testing("1e5")).unwrap();
110 assert_eq!(format!("{}", result), "100000");
111 }
112
113 #[test]
114 fn test_parse_int_scientific_negative() {
115 let result = parse_int(Fragment::testing("-1.5e3")).unwrap();
116 assert_eq!(format!("{}", result), "-1500");
117 }
118
119 #[test]
120 fn test_parse_int_float_truncation() {
121 let result = parse_int(Fragment::testing("123.789")).unwrap();
122 assert_eq!(format!("{}", result), "123");
123 }
124
125 #[test]
126 fn test_parse_int_float_truncation_negative() {
127 let result = parse_int(Fragment::testing("-123.789")).unwrap();
128 assert_eq!(format!("{}", result), "-123");
129 }
130
131 #[test]
132 fn test_parse_int_with_underscores() {
133 let result = parse_int(Fragment::testing("1_234_567")).unwrap();
134 assert_eq!(format!("{}", result), "1234567");
135 }
136
137 #[test]
138 fn test_parse_int_with_leading_space() {
139 let result = parse_int(Fragment::testing(" 12345")).unwrap();
140 assert_eq!(format!("{}", result), "12345");
141 }
142
143 #[test]
144 fn test_parse_int_with_trailing_space() {
145 let result = parse_int(Fragment::testing("12345 ")).unwrap();
146 assert_eq!(format!("{}", result), "12345");
147 }
148
149 #[test]
150 fn test_parse_int_with_both_spaces() {
151 let result = parse_int(Fragment::testing(" -12345 ")).unwrap();
152 assert_eq!(format!("{}", result), "-12345");
153 }
154
155 #[test]
156 fn test_parse_int_invalid_empty() {
157 assert!(parse_int(Fragment::testing("")).is_err());
158 }
159
160 #[test]
161 fn test_parse_int_invalid_whitespace() {
162 assert!(parse_int(Fragment::testing(" ")).is_err());
163 }
164
165 #[test]
166 fn test_parse_int_invalid_text() {
167 assert!(parse_int(Fragment::testing("abc")).is_err());
168 }
169
170 #[test]
171 fn test_parse_int_invalid_multiple_dots() {
172 assert!(parse_int(Fragment::testing("1.2.3")).is_err());
173 }
174
175 #[test]
176 fn test_parse_int_infinity() {
177 assert!(parse_int(Fragment::testing("inf")).is_err());
178 }
179
180 #[test]
181 fn test_parse_int_negative_infinity() {
182 assert!(parse_int(Fragment::testing("-inf")).is_err());
183 }
184}