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}