Skip to main content

ccxt_core/error/
parse.rs

1//! Parsing-related error types.
2
3use std::borrow::Cow;
4use thiserror::Error;
5
6/// Errors related to parsing exchange responses.
7///
8/// This type handles all parsing failures including JSON deserialization,
9/// decimal number parsing, timestamp parsing, and missing/invalid fields.
10///
11/// # Memory Optimization
12///
13/// Uses `Cow<'static, str>` for field names and messages to avoid allocation
14/// when using static strings. Use the helper constructors for ergonomic creation:
15///
16/// ```rust
17/// use ccxt_core::error::ParseError;
18///
19/// // Zero allocation (static string)
20/// let err = ParseError::missing_field("price");
21///
22/// // Allocation only when needed (dynamic string)
23/// let field_name = format!("field_{}", 42);
24/// let err = ParseError::missing_field_owned(field_name);
25///
26/// // Invalid value with context
27/// let err = ParseError::invalid_value("amount", "must be positive");
28/// ```
29///
30/// # Example
31///
32/// ```rust
33/// use ccxt_core::error::{Error, ParseError, Result};
34///
35/// fn parse_price(json: &serde_json::Value) -> Result<f64> {
36///     json.get("price")
37///         .and_then(|v| v.as_f64())
38///         .ok_or_else(|| Error::from(ParseError::missing_field("price")))
39/// }
40/// ```
41#[derive(Error, Debug)]
42#[non_exhaustive]
43pub enum ParseError {
44    /// Failed to parse decimal number.
45    #[error("Failed to parse decimal: {0}")]
46    Decimal(#[from] rust_decimal::Error),
47
48    /// Failed to deserialize JSON.
49    #[error("Failed to deserialize JSON: {0}")]
50    Json(#[from] serde_json::Error),
51
52    /// Failed to parse timestamp.
53    #[error("Failed to parse timestamp: {0}")]
54    Timestamp(Cow<'static, str>),
55
56    /// Missing required field in response.
57    #[error("Missing required field: {0}")]
58    MissingField(Cow<'static, str>),
59
60    /// Invalid value for a field.
61    #[error("Invalid value for '{field}': {message}")]
62    InvalidValue {
63        /// Field name
64        field: Cow<'static, str>,
65        /// Error message
66        message: Cow<'static, str>,
67    },
68
69    /// Invalid format for a field.
70    #[error("Invalid format for '{field}': {message}")]
71    InvalidFormat {
72        /// Field name
73        field: Cow<'static, str>,
74        /// Error message
75        message: Cow<'static, str>,
76    },
77}
78
79impl ParseError {
80    /// Creates a `MissingField` error with a static string (no allocation).
81    #[must_use]
82    pub fn missing_field(field: &'static str) -> Self {
83        Self::MissingField(Cow::Borrowed(field))
84    }
85
86    /// Creates a `MissingField` error with a dynamic string.
87    #[must_use]
88    pub fn missing_field_owned(field: String) -> Self {
89        Self::MissingField(Cow::Owned(field))
90    }
91
92    /// Creates an `InvalidValue` error.
93    pub fn invalid_value(
94        field: impl Into<Cow<'static, str>>,
95        message: impl Into<Cow<'static, str>>,
96    ) -> Self {
97        Self::InvalidValue {
98            field: field.into(),
99            message: message.into(),
100        }
101    }
102
103    /// Creates a `Timestamp` error with a static string (no allocation).
104    #[must_use]
105    pub fn timestamp(message: &'static str) -> Self {
106        Self::Timestamp(Cow::Borrowed(message))
107    }
108
109    /// Creates a `Timestamp` error with a dynamic string.
110    #[must_use]
111    pub fn timestamp_owned(message: String) -> Self {
112        Self::Timestamp(Cow::Owned(message))
113    }
114
115    /// Creates an `InvalidFormat` error.
116    pub fn invalid_format(
117        field: impl Into<Cow<'static, str>>,
118        message: impl Into<Cow<'static, str>>,
119    ) -> Self {
120        Self::InvalidFormat {
121            field: field.into(),
122            message: message.into(),
123        }
124    }
125}