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}