1use thiserror::Error;
2
3#[derive(Error, Debug)]
4pub enum DerivError {
5 #[error("WebSocket connection error: {0}")]
6 WebSocketError(#[from] tokio_tungstenite::tungstenite::Error),
7
8 #[error("URL parsing error: {0}")]
9 UrlError(#[from] url::ParseError),
10
11 #[error("JSON serialization error: {0}")]
12 SerializationError(#[from] serde_json::Error),
13
14 #[error("Invalid schema: {0}")]
15 InvalidSchema(String),
16
17 #[error("Invalid app ID: {0}")]
18 InvalidAppId(i32),
19
20 #[error("Invalid language code: {0}")]
21 InvalidLanguage(String),
22
23 #[error("Connection closed")]
24 ConnectionClosed,
25
26 #[error("Empty subscription ID")]
27 EmptySubscriptionId,
28
29 #[error("Subscription error: {0}")]
30 SubscriptionError(String),
31
32 #[error("API error: {code} - {message}")]
33 ApiError {
34 code: String,
35 message: String,
36 details: Option<serde_json::Value>,
37 },
38
39 #[error("Request timeout")]
40 Timeout,
41}
42
43pub type Result<T> = std::result::Result<T, DerivError>;
44
45#[derive(Debug, serde::Deserialize)]
47pub(crate) struct ApiErrorResponse {
48 pub error: ApiErrorDetails,
49}
50
51#[derive(Debug, serde::Deserialize)]
52pub(crate) struct ApiErrorDetails {
53 pub code: String,
54 pub message: String,
55 #[serde(default)]
56 pub details: Option<serde_json::Value>,
57}
58
59impl From<ApiErrorDetails> for DerivError {
60 fn from(error: ApiErrorDetails) -> Self {
61 DerivError::ApiError {
62 code: error.code,
63 message: error.message,
64 details: error.details,
65 }
66 }
67}
68
69pub(crate) fn parse_error(raw_response: &[u8]) -> Result<()> {
70 let response: serde_json::Value = serde_json::from_slice(raw_response)?;
71
72 if let Some(error) = response.get("error") {
73 let error_details: ApiErrorDetails = serde_json::from_value(error.clone())?;
74 return Err(error_details.into());
75 }
76
77 Ok(())
78}