1#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
2use copybook_error::{Error, ErrorCode, Result};
12
13#[inline]
18#[must_use = "Handle the Result or propagate the error"]
19pub fn safe_array_bound(
20 base: usize,
21 count: usize,
22 item_size: usize,
23 context: &str,
24) -> Result<usize> {
25 let total_size = count.checked_mul(item_size).ok_or_else(|| {
26 Error::new(
27 ErrorCode::CBKP021_ODO_NOT_TAIL,
28 format!("Array size overflow in {context}: {count} * {item_size} would overflow"),
29 )
30 })?;
31
32 base.checked_add(total_size).ok_or_else(|| {
33 Error::new(
34 ErrorCode::CBKP021_ODO_NOT_TAIL,
35 format!("Array offset overflow in {context}: {base} + {total_size} would overflow"),
36 )
37 })
38}
39
40#[inline]
45#[must_use = "Handle the Result or propagate the error"]
46pub fn safe_u64_to_u32(value: u64, context: &str) -> Result<u32> {
47 u32::try_from(value).map_err(|_| {
48 Error::new(
49 ErrorCode::CBKS141_RECORD_TOO_LARGE,
50 format!(
51 "Integer overflow converting u64 to u32 in {context}: {value} exceeds u32::MAX"
52 ),
53 )
54 })
55}
56
57#[inline]
62#[must_use = "Handle the Result or propagate the error"]
63pub fn safe_u64_to_u16(value: u64, context: &str) -> Result<u16> {
64 u16::try_from(value).map_err(|_| {
65 Error::new(
66 ErrorCode::CBKS141_RECORD_TOO_LARGE,
67 format!(
68 "Integer overflow converting u64 to u16 in {context}: {value} exceeds u16::MAX"
69 ),
70 )
71 })
72}
73
74#[inline]
79#[must_use = "Handle the Result or propagate the error"]
80pub fn safe_usize_to_u32(value: usize, context: &str) -> Result<u32> {
81 u32::try_from(value).map_err(|_| {
82 Error::new(
83 ErrorCode::CBKS141_RECORD_TOO_LARGE,
84 format!(
85 "Integer overflow converting usize to u32 in {context}: {value} exceeds u32::MAX"
86 ),
87 )
88 })
89}
90
91#[cfg(test)]
92#[allow(clippy::expect_used, clippy::unwrap_used)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn safe_array_bound_ok() {
98 let value = safe_array_bound(10, 3, 4, "test");
99 assert_eq!(value.unwrap(), 22);
100 }
101
102 #[test]
103 fn safe_array_bound_mul_overflow() {
104 let err = safe_array_bound(0, usize::MAX, 2, "mul-overflow").unwrap_err();
105 assert_eq!(err.code, ErrorCode::CBKP021_ODO_NOT_TAIL);
106 }
107
108 #[test]
109 fn safe_array_bound_add_overflow() {
110 let err = safe_array_bound(usize::MAX - 1, 1, 2, "add-overflow").unwrap_err();
111 assert_eq!(err.code, ErrorCode::CBKP021_ODO_NOT_TAIL);
112 }
113
114 #[test]
115 fn safe_u64_to_u32_ok() {
116 let value = safe_u64_to_u32(123, "test");
117 assert_eq!(value.unwrap(), 123);
118 }
119
120 #[test]
121 fn safe_u64_to_u32_overflow() {
122 let err = safe_u64_to_u32(u64::from(u32::MAX) + 1, "u64->u32").unwrap_err();
123 assert_eq!(err.code, ErrorCode::CBKS141_RECORD_TOO_LARGE);
124 }
125
126 #[test]
127 fn safe_u64_to_u16_ok() {
128 let value = safe_u64_to_u16(123, "test");
129 assert_eq!(value.unwrap(), 123);
130 }
131
132 #[test]
133 fn safe_u64_to_u16_overflow() {
134 let err = safe_u64_to_u16(u64::from(u16::MAX) + 1, "u64->u16").unwrap_err();
135 assert_eq!(err.code, ErrorCode::CBKS141_RECORD_TOO_LARGE);
136 }
137
138 #[test]
139 fn safe_usize_to_u32_ok() {
140 let value = safe_usize_to_u32(123, "test");
141 assert_eq!(value.unwrap(), 123);
142 }
143
144 #[cfg(target_pointer_width = "64")]
145 #[test]
146 fn safe_usize_to_u32_overflow() {
147 let err = safe_usize_to_u32(u32::MAX as usize + 1, "usize->u32").unwrap_err();
148 assert_eq!(err.code, ErrorCode::CBKS141_RECORD_TOO_LARGE);
149 }
150
151 #[test]
154 fn safe_u64_to_u32_at_max_boundary() {
155 assert_eq!(
156 safe_u64_to_u32(u64::from(u32::MAX), "boundary").unwrap(),
157 u32::MAX
158 );
159 }
160
161 #[test]
162 fn safe_u64_to_u32_zero() {
163 assert_eq!(safe_u64_to_u32(0, "zero").unwrap(), 0);
164 }
165
166 #[test]
167 fn safe_u64_to_u16_at_max_boundary() {
168 assert_eq!(
169 safe_u64_to_u16(u64::from(u16::MAX), "boundary").unwrap(),
170 u16::MAX
171 );
172 }
173
174 #[test]
175 fn safe_u64_to_u16_zero() {
176 assert_eq!(safe_u64_to_u16(0, "zero").unwrap(), 0);
177 }
178
179 #[test]
180 fn safe_u64_to_u16_just_over_max() {
181 let err = safe_u64_to_u16(u64::from(u16::MAX) + 1, "over-max").unwrap_err();
182 assert_eq!(err.code, ErrorCode::CBKS141_RECORD_TOO_LARGE);
183 }
184
185 #[test]
186 fn safe_u64_to_u32_just_over_max() {
187 let err = safe_u64_to_u32(u64::from(u32::MAX) + 1, "over-max").unwrap_err();
188 assert_eq!(err.code, ErrorCode::CBKS141_RECORD_TOO_LARGE);
189 }
190
191 #[test]
192 fn safe_u64_to_u32_u64_max() {
193 let err = safe_u64_to_u32(u64::MAX, "u64-max").unwrap_err();
194 assert_eq!(err.code, ErrorCode::CBKS141_RECORD_TOO_LARGE);
195 }
196
197 #[test]
198 fn safe_u64_to_u16_u64_max() {
199 let err = safe_u64_to_u16(u64::MAX, "u64-max").unwrap_err();
200 assert_eq!(err.code, ErrorCode::CBKS141_RECORD_TOO_LARGE);
201 }
202
203 #[test]
204 fn safe_usize_to_u32_zero() {
205 assert_eq!(safe_usize_to_u32(0, "zero").unwrap(), 0);
206 }
207
208 #[test]
209 fn safe_usize_to_u32_at_max_boundary() {
210 assert_eq!(
211 safe_usize_to_u32(u32::MAX as usize, "boundary").unwrap(),
212 u32::MAX
213 );
214 }
215
216 #[test]
219 fn safe_array_bound_zero_count() {
220 assert_eq!(safe_array_bound(10, 0, 4, "zero-count").unwrap(), 10);
221 }
222
223 #[test]
224 fn safe_array_bound_zero_base() {
225 assert_eq!(safe_array_bound(0, 5, 3, "zero-base").unwrap(), 15);
226 }
227
228 #[test]
229 fn safe_array_bound_zero_item_size() {
230 assert_eq!(safe_array_bound(10, 1000, 0, "zero-item").unwrap(), 10);
231 }
232
233 #[test]
234 fn safe_array_bound_all_zeros() {
235 assert_eq!(safe_array_bound(0, 0, 0, "all-zero").unwrap(), 0);
236 }
237
238 #[test]
239 fn safe_array_bound_error_message_contains_context() {
240 let err = safe_array_bound(0, usize::MAX, 2, "my-context").unwrap_err();
241 assert!(
242 err.message.contains("my-context"),
243 "Error message should contain context"
244 );
245 }
246
247 #[test]
250 fn safe_array_bound_large_non_overflowing() {
251 assert_eq!(
253 safe_array_bound(500, 1000, 1000, "large").unwrap(),
254 1_000_500
255 );
256 }
257
258 #[test]
259 fn safe_array_bound_max_base_zero_product() {
260 assert_eq!(
261 safe_array_bound(usize::MAX, 0, 100, "max-base").unwrap(),
262 usize::MAX
263 );
264 }
265
266 #[test]
267 fn safe_array_bound_mul_overflow_message() {
268 let err = safe_array_bound(0, usize::MAX, 2, "ctx").unwrap_err();
269 assert!(err.message.contains("Array size overflow"));
270 assert!(err.message.contains("would overflow"));
271 }
272
273 #[test]
274 fn safe_array_bound_add_overflow_message() {
275 let err = safe_array_bound(usize::MAX, 1, 1, "ctx").unwrap_err();
276 assert!(err.message.contains("Array offset overflow"));
277 assert!(err.message.contains("would overflow"));
278 }
279
280 #[test]
281 fn safe_u64_to_u32_midrange() {
282 assert_eq!(safe_u64_to_u32(1_000_000, "mid").unwrap(), 1_000_000);
283 }
284
285 #[test]
286 fn safe_u64_to_u32_error_message_contains_context() {
287 let err = safe_u64_to_u32(u64::from(u32::MAX) + 1, "my-ctx").unwrap_err();
288 assert!(err.message.contains("my-ctx"));
289 assert!(err.message.contains("exceeds u32::MAX"));
290 }
291
292 #[test]
293 fn safe_u64_to_u16_midrange() {
294 assert_eq!(safe_u64_to_u16(30_000, "mid").unwrap(), 30_000);
295 }
296
297 #[test]
298 fn safe_u64_to_u16_error_message_contains_context() {
299 let err = safe_u64_to_u16(70_000, "my-ctx").unwrap_err();
300 assert!(err.message.contains("my-ctx"));
301 assert!(err.message.contains("exceeds u16::MAX"));
302 }
303
304 #[test]
305 fn safe_u64_to_u16_at_u32_max() {
306 let err = safe_u64_to_u16(u64::from(u32::MAX), "u32-max").unwrap_err();
307 assert_eq!(err.code, ErrorCode::CBKS141_RECORD_TOO_LARGE);
308 }
309
310 #[test]
311 fn safe_usize_to_u32_midrange() {
312 assert_eq!(safe_usize_to_u32(50_000, "mid").unwrap(), 50_000);
313 }
314
315 #[test]
316 fn safe_usize_to_u32_error_message_contains_context() {
317 #[cfg(target_pointer_width = "64")]
318 {
319 let err = safe_usize_to_u32(u32::MAX as usize + 1, "my-ctx").unwrap_err();
320 assert!(err.message.contains("my-ctx"));
321 assert!(err.message.contains("exceeds u32::MAX"));
322 }
323 }
324
325 #[test]
326 fn safe_u64_to_u32_one() {
327 assert_eq!(safe_u64_to_u32(1, "one").unwrap(), 1);
328 }
329
330 #[test]
331 fn safe_u64_to_u16_one() {
332 assert_eq!(safe_u64_to_u16(1, "one").unwrap(), 1);
333 }
334
335 #[test]
336 fn safe_usize_to_u32_one() {
337 assert_eq!(safe_usize_to_u32(1, "one").unwrap(), 1);
338 }
339
340 #[test]
341 fn safe_array_bound_single_element() {
342 assert_eq!(safe_array_bound(0, 1, 1, "single").unwrap(), 1);
343 }
344
345 #[test]
346 fn safe_array_bound_base_at_max_minus_product() {
347 assert_eq!(
349 safe_array_bound(usize::MAX - 10, 1, 10, "exact").unwrap(),
350 usize::MAX
351 );
352 }
353}