Skip to main content

polyoxide_clob/
error.rs

1use polyoxide_core::ApiError;
2use thiserror::Error;
3
4use crate::types::ParseTickSizeError;
5
6/// Error types for CLOB API operations
7#[derive(Error, Debug)]
8pub enum ClobError {
9    /// Core API error
10    #[error(transparent)]
11    Api(#[from] ApiError),
12
13    /// Cryptographic operation failed
14    #[error("Crypto error: {0}")]
15    Crypto(String),
16
17    /// Alloy (Ethereum library) error
18    #[error("Alloy error: {0}")]
19    Alloy(String),
20
21    /// Invalid tick size
22    #[error(transparent)]
23    InvalidTickSize(#[from] ParseTickSizeError),
24}
25
26impl ClobError {
27    /// Create error from HTTP response
28    pub(crate) async fn from_response(response: reqwest::Response) -> Self {
29        Self::Api(ApiError::from_response(response).await)
30    }
31
32    /// Create validation error
33    pub(crate) fn validation(msg: impl Into<String>) -> Self {
34        Self::Api(ApiError::Validation(msg.into()))
35    }
36
37    /// Create service error (external dependency failure)
38    pub(crate) fn service(msg: impl Into<String>) -> Self {
39        Self::Api(ApiError::Api {
40            status: 0,
41            message: msg.into(),
42        })
43    }
44}
45
46impl From<alloy::signers::Error> for ClobError {
47    fn from(err: alloy::signers::Error) -> Self {
48        Self::Alloy(err.to_string())
49    }
50}
51
52impl From<alloy::hex::FromHexError> for ClobError {
53    fn from(err: alloy::hex::FromHexError) -> Self {
54        Self::Alloy(err.to_string())
55    }
56}
57
58impl From<reqwest::Error> for ClobError {
59    fn from(err: reqwest::Error) -> Self {
60        Self::Api(ApiError::Network(err))
61    }
62}
63
64impl From<url::ParseError> for ClobError {
65    fn from(err: url::ParseError) -> Self {
66        Self::Api(ApiError::Url(err))
67    }
68}
69
70impl From<serde_json::Error> for ClobError {
71    fn from(err: serde_json::Error) -> Self {
72        Self::Api(ApiError::Serialization(err))
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_service_error_is_api_not_validation() {
82        let err = ClobError::service("Gamma client failed");
83        match &err {
84            ClobError::Api(ApiError::Api { status, message }) => {
85                assert_eq!(*status, 0);
86                assert_eq!(message, "Gamma client failed");
87            }
88            other => panic!("Expected ApiError::Api, got {:?}", other),
89        }
90    }
91
92    #[test]
93    fn test_validation_error() {
94        let err = ClobError::validation("bad input");
95        match &err {
96            ClobError::Api(ApiError::Validation(msg)) => {
97                assert_eq!(msg, "bad input");
98            }
99            other => panic!("Expected ApiError::Validation, got {:?}", other),
100        }
101    }
102
103    #[test]
104    fn test_service_and_validation_are_distinct() {
105        let service = ClobError::service("service failure");
106        let validation = ClobError::validation("validation failure");
107
108        let service_msg = format!("{}", service);
109        let validation_msg = format!("{}", validation);
110
111        // They should produce different Display output
112        assert_ne!(service_msg, validation_msg);
113        assert!(service_msg.contains("service failure"));
114        assert!(validation_msg.contains("validation failure"));
115    }
116}