neo3/neo_utils/error.rs
1//! Error handling utilities for the Neo N3 SDK.
2
3/// Converts an Option to a Result with a custom error message.
4///
5/// # Examples
6///
7/// ```
8/// use neo3::prelude::*;
9/// use neo3::neo_utils::error::option_to_result;
10///
11/// let value: Option<u32> = Some(42);
12/// let result = option_to_result(value, || NeoError::Generic { message: "Value is None".to_string() });
13/// assert_eq!(result.unwrap(), 42);
14/// ```
15pub fn option_to_result<T, E, F>(option: Option<T>, err_fn: F) -> Result<T, E>
16where
17 F: FnOnce() -> E,
18{
19 option.ok_or_else(err_fn)
20}
21
22/// Adds context to an error.
23///
24/// # Examples
25///
26/// ```ignore
27/// use neo3::prelude::*;
28/// use neo3::neo_utils::error::with_context;
29///
30/// let result: Result<u32, NeoError> = Err(NeoError::Generic { message: "Original error".to_string() });
31/// let result_with_context = with_context(result, || "Additional context", |e| e);
32/// ```
33pub fn with_context<T, E, C, F, G>(
34 result: Result<T, E>,
35 context_fn: F,
36 error_mapper: G,
37) -> Result<T, E>
38where
39 E: std::fmt::Display,
40 F: FnOnce() -> C,
41 C: std::fmt::Display,
42 G: FnOnce(String) -> E,
43{
44 result.map_err(|err| {
45 let context = context_fn();
46 error_mapper(format!("{}: {}", context, err))
47 })
48}
49
50/// Converts a Result to an Option, logging the error if present.
51///
52/// # Examples
53///
54/// ```
55/// use neo3::prelude::*;
56/// use neo3::neo_utils::error::result_to_option;
57///
58/// let result: Result<u32, NeoError> = Ok(42);
59/// let option = result_to_option(result);
60/// assert_eq!(option, Some(42));
61/// ```
62pub fn result_to_option<T, E: std::fmt::Display>(result: Result<T, E>) -> Option<T> {
63 match result {
64 Ok(value) => Some(value),
65 Err(err) => {
66 eprintln!("Error: {}", err);
67 None
68 },
69 }
70}
71
72/// Attempts to execute a fallible operation multiple times before giving up.
73///
74/// # Examples
75///
76/// ```
77/// use neo3::prelude::*;
78/// use neo3::neo_utils::error::retry;
79/// use std::time::Duration;
80///
81/// async fn fallible_operation() -> Result<u32, NeoError> {
82/// // Some operation that might fail
83/// Ok(42)
84/// }
85///
86/// # async fn example() -> Result<(), NeoError> {
87/// let result = retry(
88/// || async { fallible_operation().await },
89/// 3,
90/// Duration::from_millis(100)
91/// ).await;
92/// # Ok(())
93/// # }
94/// ```
95pub async fn retry<T, E, F, Fut>(
96 operation: F,
97 max_attempts: usize,
98 delay: std::time::Duration,
99) -> Result<T, E>
100where
101 F: Fn() -> Fut,
102 Fut: std::future::Future<Output = Result<T, E>>,
103 E: std::fmt::Display,
104{
105 let mut attempts = 0;
106 let mut last_error = None;
107
108 while attempts < max_attempts {
109 match operation().await {
110 Ok(value) => return Ok(value),
111 Err(err) => {
112 attempts += 1;
113 if attempts < max_attempts {
114 eprintln!("Attempt {} failed: {}. Retrying...", attempts, err);
115 tokio::time::sleep(delay).await;
116 }
117 last_error = Some(err);
118 },
119 }
120 }
121
122 Err(last_error.expect("Should have at least one error after failed attempts"))
123}