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 use crate::host::host_bindings_trait::MockHostBindings;
221 use crate::host::setup_mock;
222 use mockall::predicate::always;
223
224 #[test]
225 fn test_match_result_code_success_positive() {
226 let result = match_result_code(5, || "success");
227 assert!(result.is_ok());
228 assert_eq!(result.unwrap(), "success");
229 }
230
231 #[test]
232 fn test_match_result_code_success_zero() {
233 let result = match_result_code(0, || "zero_success");
234 assert!(result.is_ok());
235 assert_eq!(result.unwrap(), "zero_success");
236 }
237
238 #[test]
239 fn test_match_result_code_error_negative() {
240 let result = match_result_code(INTERNAL_ERROR, || "should_not_execute");
241 assert!(result.is_err());
242 assert_eq!(result.err().unwrap().code(), INTERNAL_ERROR);
243 }
244
245 #[test]
246 fn test_match_result_code_error_field_not_found() {
247 let result = match_result_code(FIELD_NOT_FOUND, || "should_not_execute");
248 assert!(result.is_err());
249 assert_eq!(result.err().unwrap().code(), FIELD_NOT_FOUND);
250 }
251
252 #[test]
253 fn test_match_result_code_closure_not_called_on_error() {
254 let mut called = false;
255 let _result = match_result_code(BUFFER_TOO_SMALL, || {
256 called = true;
257 "should_not_execute"
258 });
259 assert!(!called);
260 }
261
262 #[test]
263 fn test_match_result_code_optional_success_some() {
264 let result = match_result_code_optional(10, || Some("data"));
265 assert!(result.is_ok());
266 assert_eq!(result.unwrap(), Some("data"));
267 }
268
269 #[test]
270 fn test_match_result_code_optional_success_none() {
271 let result = match_result_code_optional(0, || None::<&str>);
272 assert!(result.is_ok());
273 assert_eq!(result.unwrap(), None);
274 }
275
276 #[test]
277 fn test_match_result_code_optional_error() {
278 let result = match_result_code_optional(NO_ARRAY, || Some("should_not_execute"));
279 assert!(result.is_err());
280 assert_eq!(result.err().unwrap().code(), NO_ARRAY);
281 }
282
283 #[test]
284 fn test_match_result_code_with_expected_bytes_exact_match() {
285 let expected_bytes = 32;
286 let result = match_result_code_with_expected_bytes(32, expected_bytes, || "exact_match");
287 assert!(result.is_ok());
288 assert_eq!(result.unwrap(), "exact_match");
289 }
290
291 #[test]
292 fn test_match_result_code_with_expected_bytes_mismatch() {
293 let expected_bytes = 32;
294 let result =
295 match_result_code_with_expected_bytes(16, expected_bytes, || "should_not_execute");
296 assert!(result.is_err());
297 assert_eq!(result.err().unwrap().code(), INTERNAL_ERROR);
298 }
299
300 #[test]
301 fn test_match_result_code_with_expected_bytes_negative_error() {
302 let expected_bytes = 32;
303 let result = match_result_code_with_expected_bytes(
304 INVALID_PARAMS,
305 expected_bytes,
306 || "should_not_execute",
307 );
308 assert!(result.is_err());
309 assert_eq!(result.err().unwrap().code(), INVALID_PARAMS);
310 }
311
312 #[test]
313 fn test_match_result_code_with_expected_bytes_zero_bytes() {
314 let expected_bytes = 0;
315 let result = match_result_code_with_expected_bytes(0, expected_bytes, || "zero_bytes");
316 assert!(result.is_ok());
317 assert_eq!(result.unwrap(), "zero_bytes");
318 }
319
320 #[test]
321 fn test_match_result_code_with_expected_bytes_optional_exact_match_some() {
322 let expected_bytes = 20;
323 let result =
324 match_result_code_with_expected_bytes_optional(20, expected_bytes, || Some("data"));
325 assert!(result.is_ok());
326 assert_eq!(result.unwrap(), Some("data"));
327 }
328
329 #[test]
330 fn test_match_result_code_with_expected_bytes_optional_exact_match_none() {
331 let expected_bytes = 20;
332 let result =
333 match_result_code_with_expected_bytes_optional(20, expected_bytes, || None::<&str>);
334 assert!(result.is_ok());
335 assert_eq!(result.unwrap(), None);
336 }
337
338 #[test]
339 fn test_match_result_code_with_expected_bytes_optional_field_not_found() {
340 let expected_bytes = 20;
341 let result =
342 match_result_code_with_expected_bytes_optional(FIELD_NOT_FOUND, expected_bytes, || {
343 Some("should_not_execute")
344 });
345 assert!(result.is_ok());
346 assert_eq!(result.unwrap(), None);
347 }
348
349 #[test]
350 fn test_match_result_code_with_expected_bytes_optional_byte_mismatch() {
351 let mut mock = MockHostBindings::new();
352
353 mock.expect_trace_num()
355 .with(always(), always(), always())
356 .returning(|_, _, _| 0)
357 .times(2);
358
359 let _guard = setup_mock(mock);
360
361 let expected_bytes = 20;
362 let result = match_result_code_with_expected_bytes_optional(15, expected_bytes, || {
363 Some("should_not_execute")
364 });
365 assert!(result.is_err());
366 assert_eq!(result.err().unwrap().code(), POINTER_OUT_OF_BOUNDS);
367 }
368
369 #[test]
370 fn test_match_result_code_with_expected_bytes_optional_other_error() {
371 let mut mock = MockHostBindings::new();
372
373 mock.expect_trace_num()
375 .with(always(), always(), always())
376 .returning(|_, _, _| 0);
377
378 let _guard = setup_mock(mock);
379
380 let expected_bytes = 20;
381 let result =
382 match_result_code_with_expected_bytes_optional(INVALID_ACCOUNT, expected_bytes, || {
383 Some("should_not_execute")
384 });
385 assert!(result.is_err());
386 assert_eq!(result.err().unwrap().code(), INVALID_ACCOUNT);
387 }
388
389 #[test]
390 fn test_match_result_code_with_expected_bytes_optional_zero_bytes() {
391 let expected_bytes = 0;
392 let result =
393 match_result_code_with_expected_bytes_optional(0, expected_bytes, || Some("zero_data"));
394 assert!(result.is_ok());
395 assert_eq!(result.unwrap(), Some("zero_data"));
396 }
397
398 #[test]
399 fn test_all_error_constants_are_negative() {
400 let error_codes = [
401 INTERNAL_ERROR,
402 FIELD_NOT_FOUND,
403 BUFFER_TOO_SMALL,
404 NO_ARRAY,
405 NOT_LEAF_FIELD,
406 LOCATOR_MALFORMED,
407 SLOT_OUT_RANGE,
408 SLOTS_FULL,
409 EMPTY_SLOT,
410 LEDGER_OBJ_NOT_FOUND,
411 INVALID_DECODING,
412 DATA_FIELD_TOO_LARGE,
413 POINTER_OUT_OF_BOUNDS,
414 NO_MEM_EXPORTED,
415 INVALID_PARAMS,
416 INVALID_ACCOUNT,
417 INVALID_FIELD,
418 INDEX_OUT_OF_BOUNDS,
419 INVALID_FLOAT_INPUT,
420 INVALID_FLOAT_COMPUTATION,
421 ];
422
423 for &code in &error_codes {
424 assert!(code < 0, "Error code {} should be negative", code);
425 }
426 }
427
428 #[test]
429 fn test_error_constants_are_unique() {
430 let error_codes = [
431 INTERNAL_ERROR,
432 FIELD_NOT_FOUND,
433 BUFFER_TOO_SMALL,
434 NO_ARRAY,
435 NOT_LEAF_FIELD,
436 LOCATOR_MALFORMED,
437 SLOT_OUT_RANGE,
438 SLOTS_FULL,
439 EMPTY_SLOT,
440 LEDGER_OBJ_NOT_FOUND,
441 INVALID_DECODING,
442 DATA_FIELD_TOO_LARGE,
443 POINTER_OUT_OF_BOUNDS,
444 NO_MEM_EXPORTED,
445 INVALID_PARAMS,
446 INVALID_ACCOUNT,
447 INVALID_FIELD,
448 INDEX_OUT_OF_BOUNDS,
449 INVALID_FLOAT_INPUT,
450 INVALID_FLOAT_COMPUTATION,
451 ];
452
453 for (i, &code1) in error_codes.iter().enumerate() {
455 for (j, &code2) in error_codes.iter().enumerate() {
456 if i != j {
457 assert_ne!(
458 code1, code2,
459 "Error codes at indices {} and {} are not unique: {} == {}",
460 i, j, code1, code2
461 );
462 }
463 }
464 }
465 }
466
467 #[test]
468 fn test_error_from_code_roundtrip() {
469 let test_codes = [
470 INTERNAL_ERROR,
471 FIELD_NOT_FOUND,
472 BUFFER_TOO_SMALL,
473 NO_ARRAY,
474 NOT_LEAF_FIELD,
475 LOCATOR_MALFORMED,
476 SLOT_OUT_RANGE,
477 SLOTS_FULL,
478 EMPTY_SLOT,
479 LEDGER_OBJ_NOT_FOUND,
480 INVALID_DECODING,
481 DATA_FIELD_TOO_LARGE,
482 POINTER_OUT_OF_BOUNDS,
483 NO_MEM_EXPORTED,
484 INVALID_PARAMS,
485 INVALID_ACCOUNT,
486 INVALID_FIELD,
487 INDEX_OUT_OF_BOUNDS,
488 INVALID_FLOAT_INPUT,
489 INVALID_FLOAT_COMPUTATION,
490 ];
491
492 for &code in &test_codes {
493 let error = Error::from_code(code);
494 assert_eq!(
495 error.code(),
496 code,
497 "Error code roundtrip failed for code {}",
498 code
499 );
500 }
501 }
502
503 #[test]
504 fn test_closure_execution_count() {
505 let mut execution_count = 0;
506 let closure = || {
507 execution_count += 1;
508 "executed"
509 };
510
511 let _result = match_result_code(1, closure);
513 assert_eq!(execution_count, 1);
514
515 execution_count = 0;
517 let closure = || {
518 execution_count += 1;
519 "should_not_execute"
520 };
521 let _result = match_result_code(INTERNAL_ERROR, closure);
522 assert_eq!(execution_count, 0);
523 }
524
525 #[test]
526 fn test_large_positive_result_codes() {
527 let large_positive = 1024;
529 let result = match_result_code(large_positive, || "large_success");
530 assert!(result.is_ok());
531 assert_eq!(result.unwrap(), "large_success");
532
533 let result = match_result_code_with_expected_bytes(
535 large_positive,
536 large_positive as usize,
537 || "exact_large",
538 );
539 assert!(result.is_ok());
540 assert_eq!(result.unwrap(), "exact_large");
541 }
542
543 #[test]
544 fn test_edge_case_usize_conversion() {
545 let result_code = 255i32;
547 let expected_bytes = 255usize;
548 let result =
549 match_result_code_with_expected_bytes(result_code, expected_bytes, || "converted");
550 assert!(result.is_ok());
551 assert_eq!(result.unwrap(), "converted");
552 }
553}