ccxt_core/error/context.rs
1//! Context attachment trait and implementations.
2
3use crate::error::{Error, Result};
4use std::fmt;
5
6/// Extension trait for ergonomic error context attachment.
7///
8/// This trait provides methods to add context to errors, making it easier
9/// to understand where and why an error occurred. It works with both
10/// `Result<T, E>` and `Option<T>` types.
11///
12/// # When to Use
13///
14/// - Use `context()` when you have a static context message
15/// - Use `with_context()` when the context message is expensive to compute
16/// (it's only evaluated on error)
17///
18/// # Library vs Application Code
19///
20/// - **Library code**: Use this trait for adding context within the library
21/// - **Application code**: Consider using `anyhow::Context` for richer error handling
22///
23/// # Examples
24///
25/// ## Adding Context to Results
26///
27/// ```rust
28/// use ccxt_core::error::{Error, Result, ContextExt};
29///
30/// fn fetch_ticker(symbol: &str) -> Result<f64> {
31/// // Static context (always evaluated)
32/// let data = fetch_raw_data()
33/// .context("Failed to fetch raw data")?;
34///
35/// // Lazy context (only evaluated on error)
36/// parse_price(&data)
37/// .with_context(|| format!("Failed to parse price for {}", symbol))
38/// }
39/// # fn fetch_raw_data() -> Result<String> { Ok("{}".to_string()) }
40/// # fn parse_price(_: &str) -> Result<f64> { Ok(42.0) }
41/// ```
42///
43/// ## Adding Context to Options
44///
45/// ```rust
46/// use ccxt_core::error::{Result, ContextExt};
47///
48/// fn get_required_field(json: &serde_json::Value) -> Result<&str> {
49/// json.get("field")
50/// .and_then(|v| v.as_str())
51/// .context("Missing required field 'field'")
52/// }
53/// ```
54pub trait ContextExt<T, E> {
55 /// Adds context to an error.
56 fn context<C>(self, context: C) -> Result<T>
57 where
58 C: fmt::Display + Send + Sync + 'static;
59
60 /// Adds lazy context to an error (only evaluated on error).
61 fn with_context<C, F>(self, f: F) -> Result<T>
62 where
63 C: fmt::Display + Send + Sync + 'static,
64 F: FnOnce() -> C;
65}
66
67impl<T, E> ContextExt<T, E> for std::result::Result<T, E>
68where
69 E: Into<Error>,
70{
71 fn context<C>(self, context: C) -> Result<T>
72 where
73 C: fmt::Display + Send + Sync + 'static,
74 {
75 self.map_err(|e| e.into().context(context.to_string()))
76 }
77
78 fn with_context<C, F>(self, f: F) -> Result<T>
79 where
80 C: fmt::Display + Send + Sync + 'static,
81 F: FnOnce() -> C,
82 {
83 self.map_err(|e| e.into().context(f().to_string()))
84 }
85}
86
87impl<T> ContextExt<T, Error> for Option<T> {
88 fn context<C>(self, context: C) -> Result<T>
89 where
90 C: fmt::Display + Send + Sync + 'static,
91 {
92 self.ok_or_else(|| Error::generic(context.to_string()))
93 }
94
95 fn with_context<C, F>(self, f: F) -> Result<T>
96 where
97 C: fmt::Display + Send + Sync + 'static,
98 F: FnOnce() -> C,
99 {
100 self.ok_or_else(|| Error::generic(f().to_string()))
101 }
102}
103
104/// Helper trait for adding context to errors (legacy alias for ContextExt).
105#[deprecated(since = "0.2.0", note = "Use ContextExt instead")]
106pub trait ErrorContext<T>: Sized {
107 /// Add context to an error
108 fn context(self, context: impl fmt::Display) -> Result<T>;
109}
110
111#[allow(deprecated)]
112impl<T, E: Into<Error>> ErrorContext<T> for std::result::Result<T, E> {
113 fn context(self, context: impl fmt::Display) -> Result<T> {
114 self.map_err(|e| e.into().context(context.to_string()))
115 }
116}