Skip to main content

tvdata_rs/
error.rs

1use std::time::Duration;
2
3use reqwest::StatusCode;
4use thiserror::Error;
5
6pub type Result<T> = std::result::Result<T, Error>;
7
8#[derive(Debug, Error)]
9pub enum Error {
10    #[error("http request failed: {0}")]
11    Http(#[source] Box<reqwest_middleware::Error>),
12
13    #[error("websocket request failed: {0}")]
14    WebSocket(#[source] Box<tokio_tungstenite::tungstenite::Error>),
15
16    #[error("failed to deserialize tradingview payload: {0}")]
17    Json(#[from] serde_json::Error),
18
19    #[error("failed to format time value: {0}")]
20    TimeFormat(#[from] time::error::Format),
21
22    #[error("invalid endpoint url: {0}")]
23    UrlParse(#[from] url::ParseError),
24
25    #[error("tradingview returned an API error: {0}")]
26    ApiMessage(String),
27
28    #[error("tradingview returned HTTP {status}: {body}")]
29    ApiStatus { status: StatusCode, body: String },
30
31    #[error("search query cannot be empty")]
32    EmptySearchQuery,
33
34    #[error("scan page limit must be greater than zero")]
35    InvalidPageLimit,
36
37    #[error("history request returned no bars for {symbol}")]
38    HistoryEmpty { symbol: String },
39
40    #[error("scan returned no rows for {symbol}")]
41    SymbolNotFound { symbol: String },
42
43    #[error("scan validation is unavailable: {reason}")]
44    ScanValidationUnavailable { reason: String },
45
46    #[error("scan query uses fields unsupported for {route}: {fields:?}")]
47    UnsupportedScanFields { route: String, fields: Vec<String> },
48
49    #[error("quote session returned no data for {symbol}")]
50    QuoteEmpty { symbol: String },
51
52    #[error("quote session returned status {status} for {symbol}")]
53    QuoteSymbolFailed { symbol: String, status: String },
54
55    #[error("history batch concurrency must be greater than zero")]
56    InvalidBatchConcurrency,
57
58    #[error("history pagination exceeded safe limit for {symbol} after {rounds} rounds")]
59    HistoryPaginationLimitExceeded { symbol: String, rounds: usize },
60
61    #[error("history download failed for {symbol}: {source}")]
62    HistoryDownloadFailed {
63        symbol: String,
64        #[source]
65        source: Box<Error>,
66    },
67
68    #[error("retry min interval {min:?} cannot exceed max interval {max:?}")]
69    InvalidRetryBounds { min: Duration, max: Duration },
70
71    #[error("invalid websocket frame: {0}")]
72    Protocol(&'static str),
73}
74
75impl From<reqwest_middleware::Error> for Error {
76    fn from(value: reqwest_middleware::Error) -> Self {
77        Self::Http(Box::new(value))
78    }
79}
80
81impl From<reqwest::Error> for Error {
82    fn from(value: reqwest::Error) -> Self {
83        let error: reqwest_middleware::Error = value.into();
84        Self::Http(Box::new(error))
85    }
86}
87
88impl From<tokio_tungstenite::tungstenite::Error> for Error {
89    fn from(value: tokio_tungstenite::tungstenite::Error) -> Self {
90        Self::WebSocket(Box::new(value))
91    }
92}