1use crate::host::Error::{InternalError, PointerOutOfBounds};
2use crate::host::trace::trace_num;
3use crate::host::{Error, Result, Result::Err, Result::Ok};
4
5pub const INTERNAL_ERROR: i32 = -1;
7pub const FIELD_NOT_FOUND: i32 = -2;
9pub const BUFFER_TOO_SMALL: i32 = -3;
11pub const NO_ARRAY: i32 = -4;
13pub const NOT_LEAF_FIELD: i32 = -5;
15pub const LOCATOR_MALFORMED: i32 = -6;
17pub const SLOT_OUT_RANGE: i32 = -7;
19pub const SLOTS_FULL: i32 = -8;
21pub const EMPTY_SLOT: i32 = -9;
23pub const LEDGER_OBJ_NOT_FOUND: i32 = -10;
25pub const INVALID_DECODING: i32 = -11;
27pub const DATA_FIELD_TOO_LARGE: i32 = -12;
29pub const POINTER_OUT_OF_BOUNDS: i32 = -13;
31pub const NO_MEM_EXPORTED: i32 = -14;
33pub const INVALID_PARAMS: i32 = -15;
35pub const INVALID_ACCOUNT: i32 = -16;
37pub const INVALID_FIELD: i32 = -17;
39pub const INDEX_OUT_OF_BOUNDS: i32 = -18;
41pub const INVALID_FLOAT_INPUT: i32 = -19;
43pub const INVALID_FLOAT_COMPUTATION: i32 = -20;
45
46#[inline(always)]
69pub fn match_result_code<F, T>(result_code: i32, on_success: F) -> Result<T>
70where
71 F: FnOnce() -> T,
72{
73 match result_code {
74 code if code >= 0 => Ok(on_success()),
75 code => Err(Error::from_code(code)),
76 }
77}
78
79#[inline(always)]
107pub fn match_result_code_optional<F, T>(result_code: i32, on_success: F) -> Result<Option<T>>
108where
109 F: FnOnce() -> Option<T>,
110{
111 match result_code {
112 code if code >= 0 => Ok(on_success()),
113 code => Err(Error::from_code(code)),
114 }
115}
116
117#[inline]
142pub fn match_result_code_with_expected_bytes<F, T>(
143 result_code: i32,
144 expected_num_bytes: usize,
145 on_success: F,
146) -> Result<T>
147where
148 F: FnOnce() -> T,
149{
150 match result_code {
151 code if code as usize == expected_num_bytes => Ok(on_success()),
152 code if code >= 0 => Err(InternalError), code => Err(Error::from_code(code)),
154 }
155}
156
157#[inline]
188pub fn match_result_code_with_expected_bytes_optional<F, T>(
189 result_code: i32,
190 expected_num_bytes: usize,
191 on_success: F,
192) -> Result<Option<T>>
193where
194 F: FnOnce() -> Option<T>,
195{
196 match result_code {
197 code if code as usize == expected_num_bytes => Ok(on_success()),
198 code if code == FIELD_NOT_FOUND => Ok(None),
199 code if code >= 0 => {
201 let _ = trace_num(
202 "Byte array was expected to have this many bytes: ",
203 expected_num_bytes as i64,
204 );
205 let _ = trace_num("Byte array had this many bytes: ", code as i64);
206 Err(PointerOutOfBounds)
207 }
208 code => {
210 let _ = trace_num("Encountered error_code:", code as i64);
211 Err(Error::from_code(code))
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219 use crate::host::Error;
220
221 #[test]
222 fn test_match_result_code_success_positive() {
223 let result = match_result_code(5, || "success");
224 assert!(result.is_ok());
225 assert_eq!(result.unwrap(), "success");
226 }
227
228 #[test]
229 fn test_match_result_code_success_zero() {
230 let result = match_result_code(0, || "zero_success");
231 assert!(result.is_ok());
232 assert_eq!(result.unwrap(), "zero_success");
233 }
234
235 #[test]
236 fn test_match_result_code_error_negative() {
237 let result = match_result_code(INTERNAL_ERROR, || "should_not_execute");
238 assert!(result.is_err());
239 assert_eq!(result.err().unwrap().code(), INTERNAL_ERROR);
240 }
241
242 #[test]
243 fn test_match_result_code_error_field_not_found() {
244 let result = match_result_code(FIELD_NOT_FOUND, || "should_not_execute");
245 assert!(result.is_err());
246 assert_eq!(result.err().unwrap().code(), FIELD_NOT_FOUND);
247 }
248
249 #[test]
250 fn test_match_result_code_closure_not_called_on_error() {
251 let mut called = false;
252 let _result = match_result_code(BUFFER_TOO_SMALL, || {
253 called = true;
254 "should_not_execute"
255 });
256 assert!(!called);
257 }
258
259 #[test]
260 fn test_match_result_code_optional_success_some() {
261 let result = match_result_code_optional(10, || Some("data"));
262 assert!(result.is_ok());
263 assert_eq!(result.unwrap(), Some("data"));
264 }
265
266 #[test]
267 fn test_match_result_code_optional_success_none() {
268 let result = match_result_code_optional(0, || None::<&str>);
269 assert!(result.is_ok());
270 assert_eq!(result.unwrap(), None);
271 }
272
273 #[test]
274 fn test_match_result_code_optional_error() {
275 let result = match_result_code_optional(NO_ARRAY, || Some("should_not_execute"));
276 assert!(result.is_err());
277 assert_eq!(result.err().unwrap().code(), NO_ARRAY);
278 }
279
280 #[test]
281 fn test_match_result_code_with_expected_bytes_exact_match() {
282 let expected_bytes = 32;
283 let result = match_result_code_with_expected_bytes(32, expected_bytes, || "exact_match");
284 assert!(result.is_ok());
285 assert_eq!(result.unwrap(), "exact_match");
286 }
287
288 #[test]
289 fn test_match_result_code_with_expected_bytes_mismatch() {
290 let expected_bytes = 32;
291 let result =
292 match_result_code_with_expected_bytes(16, expected_bytes, || "should_not_execute");
293 assert!(result.is_err());
294 assert_eq!(result.err().unwrap().code(), INTERNAL_ERROR);
295 }
296
297 #[test]
298 fn test_match_result_code_with_expected_bytes_negative_error() {
299 let expected_bytes = 32;
300 let result = match_result_code_with_expected_bytes(
301 INVALID_PARAMS,
302 expected_bytes,
303 || "should_not_execute",
304 );
305 assert!(result.is_err());
306 assert_eq!(result.err().unwrap().code(), INVALID_PARAMS);
307 }
308
309 #[test]
310 fn test_match_result_code_with_expected_bytes_zero_bytes() {
311 let expected_bytes = 0;
312 let result = match_result_code_with_expected_bytes(0, expected_bytes, || "zero_bytes");
313 assert!(result.is_ok());
314 assert_eq!(result.unwrap(), "zero_bytes");
315 }
316
317 #[test]
318 fn test_match_result_code_with_expected_bytes_optional_exact_match_some() {
319 let expected_bytes = 20;
320 let result =
321 match_result_code_with_expected_bytes_optional(20, expected_bytes, || Some("data"));
322 assert!(result.is_ok());
323 assert_eq!(result.unwrap(), Some("data"));
324 }
325
326 #[test]
327 fn test_match_result_code_with_expected_bytes_optional_exact_match_none() {
328 let expected_bytes = 20;
329 let result =
330 match_result_code_with_expected_bytes_optional(20, expected_bytes, || None::<&str>);
331 assert!(result.is_ok());
332 assert_eq!(result.unwrap(), None);
333 }
334
335 #[test]
336 fn test_match_result_code_with_expected_bytes_optional_field_not_found() {
337 let expected_bytes = 20;
338 let result =
339 match_result_code_with_expected_bytes_optional(FIELD_NOT_FOUND, expected_bytes, || {
340 Some("should_not_execute")
341 });
342 assert!(result.is_ok());
343 assert_eq!(result.unwrap(), None);
344 }
345
346 #[test]
347 fn test_match_result_code_with_expected_bytes_optional_byte_mismatch() {
348 let expected_bytes = 20;
349 let result = match_result_code_with_expected_bytes_optional(15, expected_bytes, || {
350 Some("should_not_execute")
351 });
352 assert!(result.is_err());
353 assert_eq!(result.err().unwrap().code(), POINTER_OUT_OF_BOUNDS);
354 }
355
356 #[test]
357 fn test_match_result_code_with_expected_bytes_optional_other_error() {
358 let expected_bytes = 20;
359 let result =
360 match_result_code_with_expected_bytes_optional(INVALID_ACCOUNT, expected_bytes, || {
361 Some("should_not_execute")
362 });
363 assert!(result.is_err());
364 assert_eq!(result.err().unwrap().code(), INVALID_ACCOUNT);
365 }
366
367 #[test]
368 fn test_match_result_code_with_expected_bytes_optional_zero_bytes() {
369 let expected_bytes = 0;
370 let result =
371 match_result_code_with_expected_bytes_optional(0, expected_bytes, || Some("zero_data"));
372 assert!(result.is_ok());
373 assert_eq!(result.unwrap(), Some("zero_data"));
374 }
375
376 #[test]
377 fn test_all_error_constants_are_negative() {
378 let error_codes = [
379 INTERNAL_ERROR,
380 FIELD_NOT_FOUND,
381 BUFFER_TOO_SMALL,
382 NO_ARRAY,
383 NOT_LEAF_FIELD,
384 LOCATOR_MALFORMED,
385 SLOT_OUT_RANGE,
386 SLOTS_FULL,
387 EMPTY_SLOT,
388 LEDGER_OBJ_NOT_FOUND,
389 INVALID_DECODING,
390 DATA_FIELD_TOO_LARGE,
391 POINTER_OUT_OF_BOUNDS,
392 NO_MEM_EXPORTED,
393 INVALID_PARAMS,
394 INVALID_ACCOUNT,
395 INVALID_FIELD,
396 INDEX_OUT_OF_BOUNDS,
397 INVALID_FLOAT_INPUT,
398 INVALID_FLOAT_COMPUTATION,
399 ];
400
401 for &code in &error_codes {
402 assert!(code < 0, "Error code {} should be negative", code);
403 }
404 }
405
406 #[test]
407 fn test_error_constants_are_unique() {
408 let error_codes = [
409 INTERNAL_ERROR,
410 FIELD_NOT_FOUND,
411 BUFFER_TOO_SMALL,
412 NO_ARRAY,
413 NOT_LEAF_FIELD,
414 LOCATOR_MALFORMED,
415 SLOT_OUT_RANGE,
416 SLOTS_FULL,
417 EMPTY_SLOT,
418 LEDGER_OBJ_NOT_FOUND,
419 INVALID_DECODING,
420 DATA_FIELD_TOO_LARGE,
421 POINTER_OUT_OF_BOUNDS,
422 NO_MEM_EXPORTED,
423 INVALID_PARAMS,
424 INVALID_ACCOUNT,
425 INVALID_FIELD,
426 INDEX_OUT_OF_BOUNDS,
427 INVALID_FLOAT_INPUT,
428 INVALID_FLOAT_COMPUTATION,
429 ];
430
431 for (i, &code1) in error_codes.iter().enumerate() {
433 for (j, &code2) in error_codes.iter().enumerate() {
434 if i != j {
435 assert_ne!(
436 code1, code2,
437 "Error codes at indices {} and {} are not unique: {} == {}",
438 i, j, code1, code2
439 );
440 }
441 }
442 }
443 }
444
445 #[test]
446 fn test_error_from_code_roundtrip() {
447 let test_codes = [
448 INTERNAL_ERROR,
449 FIELD_NOT_FOUND,
450 BUFFER_TOO_SMALL,
451 NO_ARRAY,
452 NOT_LEAF_FIELD,
453 LOCATOR_MALFORMED,
454 SLOT_OUT_RANGE,
455 SLOTS_FULL,
456 EMPTY_SLOT,
457 LEDGER_OBJ_NOT_FOUND,
458 INVALID_DECODING,
459 DATA_FIELD_TOO_LARGE,
460 POINTER_OUT_OF_BOUNDS,
461 NO_MEM_EXPORTED,
462 INVALID_PARAMS,
463 INVALID_ACCOUNT,
464 INVALID_FIELD,
465 INDEX_OUT_OF_BOUNDS,
466 INVALID_FLOAT_INPUT,
467 INVALID_FLOAT_COMPUTATION,
468 ];
469
470 for &code in &test_codes {
471 let error = Error::from_code(code);
472 assert_eq!(
473 error.code(),
474 code,
475 "Error code roundtrip failed for code {}",
476 code
477 );
478 }
479 }
480
481 #[test]
482 fn test_closure_execution_count() {
483 let mut execution_count = 0;
484 let closure = || {
485 execution_count += 1;
486 "executed"
487 };
488
489 let _result = match_result_code(1, closure);
491 assert_eq!(execution_count, 1);
492
493 execution_count = 0;
495 let closure = || {
496 execution_count += 1;
497 "should_not_execute"
498 };
499 let _result = match_result_code(INTERNAL_ERROR, closure);
500 assert_eq!(execution_count, 0);
501 }
502
503 #[test]
504 fn test_large_positive_result_codes() {
505 let large_positive = 1024;
507 let result = match_result_code(large_positive, || "large_success");
508 assert!(result.is_ok());
509 assert_eq!(result.unwrap(), "large_success");
510
511 let result = match_result_code_with_expected_bytes(
513 large_positive,
514 large_positive as usize,
515 || "exact_large",
516 );
517 assert!(result.is_ok());
518 assert_eq!(result.unwrap(), "exact_large");
519 }
520
521 #[test]
522 fn test_edge_case_usize_conversion() {
523 let result_code = 255i32;
525 let expected_bytes = 255usize;
526 let result =
527 match_result_code_with_expected_bytes(result_code, expected_bytes, || "converted");
528 assert!(result.is_ok());
529 assert_eq!(result.unwrap(), "converted");
530 }
531}