1use anyhow::{Context, Result, bail};
16
17pub struct IntHandler;
21
22impl IntHandler {
23 pub fn validate_and_canonicalize(
45 value: &str,
46 range: Option<&[i64; 2]>,
47 field_name: &str,
48 ) -> Result<String> {
49 let parsed_value: i64 = value.parse().context(format!(
51 "Field '{}' must be a valid integer, got: '{}'",
52 field_name, value
53 ))?;
54
55 if let Some([min, max]) = range {
57 if parsed_value < *min || parsed_value > *max {
58 bail!(
59 "Field '{}' value {} is outside allowed range [{}, {}]",
60 field_name,
61 parsed_value,
62 min,
63 max
64 );
65 }
66
67 tracing::debug!(
68 field_name = field_name,
69 input_value = value,
70 parsed_value = parsed_value,
71 min_allowed = min,
72 max_allowed = max,
73 "Integer successfully validated within range"
74 );
75 } else {
76 tracing::debug!(
77 field_name = field_name,
78 input_value = value,
79 parsed_value = parsed_value,
80 "Integer successfully validated (no range constraint)"
81 );
82 }
83
84 Ok(parsed_value.to_string())
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_valid_positive_integer() {
96 let result = IntHandler::validate_and_canonicalize("42", None, "count");
97 assert!(result.is_ok());
98 assert_eq!(result.unwrap(), "42");
99 }
100
101 #[test]
102 fn test_valid_negative_integer() {
103 let result = IntHandler::validate_and_canonicalize("-42", None, "offset");
104 assert!(result.is_ok());
105 assert_eq!(result.unwrap(), "-42");
106 }
107
108 #[test]
109 fn test_valid_zero() {
110 let result = IntHandler::validate_and_canonicalize("0", Some(&[-10, 10]), "value");
111 assert!(result.is_ok());
112 assert_eq!(result.unwrap(), "0");
113 }
114
115 #[test]
116 fn test_valid_integer_within_range() {
117 let result = IntHandler::validate_and_canonicalize("50", Some(&[0, 100]), "step");
118 assert!(result.is_ok());
119 assert_eq!(result.unwrap(), "50");
120 }
121
122 #[test]
123 fn test_valid_integer_at_range_boundaries() {
124 let result = IntHandler::validate_and_canonicalize("0", Some(&[0, 100]), "step");
126 assert!(result.is_ok());
127 assert_eq!(result.unwrap(), "0");
128
129 let result = IntHandler::validate_and_canonicalize("100", Some(&[0, 100]), "step");
131 assert!(result.is_ok());
132 assert_eq!(result.unwrap(), "100");
133 }
134
135 #[test]
136 fn test_integer_below_minimum() {
137 let result = IntHandler::validate_and_canonicalize("-5", Some(&[0, 100]), "step");
138 assert!(result.is_err());
139 }
140
141 #[test]
142 fn test_integer_above_maximum() {
143 let result = IntHandler::validate_and_canonicalize("150", Some(&[0, 100]), "step");
144 assert!(result.is_err());
145 }
146
147 #[test]
148 fn test_invalid_integer_format() {
149 let result = IntHandler::validate_and_canonicalize("abc", None, "count");
150 assert!(result.is_err());
151 }
152
153 #[test]
154 fn test_decimal_number_rejected() {
155 let result = IntHandler::validate_and_canonicalize("42.5", None, "count");
156 assert!(result.is_err());
157 }
158
159 #[test]
160 fn test_leading_zeros_canonicalized() {
161 let result = IntHandler::validate_and_canonicalize("0042", None, "count");
162 assert!(result.is_ok());
163 assert_eq!(result.unwrap(), "42"); }
165
166 #[test]
167 fn test_large_integer() {
168 let result = IntHandler::validate_and_canonicalize("9223372036854775807", None, "big");
169 assert!(result.is_ok());
170 assert_eq!(result.unwrap(), "9223372036854775807");
171 }
172}