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}