1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3use ustr::Ustr;
4
5#[derive(Debug, Clone, Error, Copy, Serialize, Deserialize)]
15pub enum Error {
16 #[error("Generic: {0}")]
17 Internal(Ustr),
18
19 #[error("Request failed: {0}")]
20 Request(Ustr),
21
22 #[error("Rate limited: {0}")]
23 RateLimited(Ustr),
24
25 #[error("JSON parsing failed: {0}")]
26 JsonParse(Ustr),
27
28 #[error("Type conversion failed: {0}")]
29 TypeConversion(Ustr),
30
31 #[error("Invalid header value: {0}")]
32 HeaderValue(Ustr),
33
34 #[error("Login failed: {source}")]
35 Login {
36 #[source]
37 source: LoginError,
38 },
39
40 #[error("Regex error: {0}")]
41 Regex(Ustr),
42
43 #[error("WebSocket connection failed: {0}")]
44 WebSocket(Ustr),
45
46 #[error("No chart token found")]
47 NoChartTokenFound,
48
49 #[error("No scan data found")]
50 NoScanDataFound,
51
52 #[error("Symbols are not in the same exchange")]
53 SymbolsNotInSameExchange,
54
55 #[error("Exchange not specified")]
56 ExchangeNotSpecified,
57
58 #[error("Invalid exchange")]
59 InvalidExchange,
60
61 #[error("Symbols not specified")]
62 SymbolsNotSpecified,
63
64 #[error("No search data found")]
65 NoSearchDataFound,
66
67 #[error("Indicator not found or unsupported: {0}")]
68 IndicatorDataNotFound(Ustr),
69
70 #[error("Task join failed: {0}")]
71 TokioJoin(Ustr),
72
73 #[error("URL parsing failed: {0}")]
74 UrlParse(Ustr),
75
76 #[error("Date/time parsing failed: {0}")]
77 ChronoParse(Ustr),
78
79 #[error("Date/time out of range: {0}")]
80 ChronoOutOfRange(Ustr),
81
82 #[error("Timeout: {0}")]
83 Timeout(Ustr),
84
85 #[error("I/O error: {0}")]
86 Io(Ustr),
87
88 #[error("TradingView error: {source}")]
89 TradingView {
90 #[source]
91 source: TradingViewError,
92 },
93}
94
95impl From<reqwest::Error> for Error {
97 fn from(err: reqwest::Error) -> Self {
98 if let Some(status) = err.status()
99 && status == reqwest::StatusCode::TOO_MANY_REQUESTS
100 {
101 return Error::RateLimited(err.to_string().into());
102 }
103 Error::Request(err.to_string().into())
104 }
105}
106
107impl From<serde_json::Error> for Error {
108 fn from(err: serde_json::Error) -> Self {
109 Error::JsonParse(err.to_string().into())
110 }
111}
112
113impl From<std::num::ParseIntError> for Error {
114 fn from(err: std::num::ParseIntError) -> Self {
115 Error::TypeConversion(err.to_string().into())
116 }
117}
118
119impl From<reqwest::header::InvalidHeaderValue> for Error {
120 fn from(err: reqwest::header::InvalidHeaderValue) -> Self {
121 Error::HeaderValue(err.to_string().into())
122 }
123}
124
125impl From<LoginError> for Error {
126 fn from(err: LoginError) -> Self {
127 Error::Login { source: err }
128 }
129}
130
131impl From<regex::Error> for Error {
132 fn from(err: regex::Error) -> Self {
133 Error::Regex(err.to_string().into())
134 }
135}
136
137impl From<tokio_tungstenite::tungstenite::Error> for Error {
138 fn from(err: tokio_tungstenite::tungstenite::Error) -> Self {
139 Error::WebSocket(err.to_string().into())
140 }
141}
142
143impl From<tokio::task::JoinError> for Error {
144 fn from(err: tokio::task::JoinError) -> Self {
145 Error::TokioJoin(err.to_string().into())
146 }
147}
148
149impl From<url::ParseError> for Error {
150 fn from(err: url::ParseError) -> Self {
151 Error::UrlParse(err.to_string().into())
152 }
153}
154
155impl From<chrono::ParseError> for Error {
156 fn from(err: chrono::ParseError) -> Self {
157 Error::ChronoParse(err.to_string().into())
158 }
159}
160
161impl From<chrono::OutOfRangeError> for Error {
162 fn from(err: chrono::OutOfRangeError) -> Self {
163 Error::ChronoOutOfRange(err.to_string().into())
164 }
165}
166
167impl From<std::io::Error> for Error {
168 fn from(err: std::io::Error) -> Self {
169 Error::Io(err.to_string().into())
170 }
171}
172
173impl From<TradingViewError> for Error {
174 fn from(err: TradingViewError) -> Self {
175 Error::TradingView { source: err }
176 }
177}
178
179impl From<String> for Error {
180 fn from(err: String) -> Self {
181 Error::Internal(err.into())
182 }
183}
184
185impl From<&str> for Error {
186 fn from(err: &str) -> Self {
187 Error::Internal(Ustr::from(err))
188 }
189}
190
191impl From<Ustr> for Error {
192 fn from(err: Ustr) -> Self {
193 Error::Internal(err)
194 }
195}
196
197#[derive(Debug, Clone, Error, PartialEq, Eq, Hash, Copy, Serialize, Deserialize)]
202pub enum TradingViewError {
203 #[error("Series error")]
204 SeriesError,
205 #[error("Symbol error")]
206 SymbolError,
207 #[error("Critical error")]
208 CriticalError,
209 #[error("Study error")]
210 StudyError,
211 #[error("Protocol error")]
212 ProtocolError,
213 #[error("Quote data status error: {0}")]
214 QuoteDataStatusError(Ustr),
215 #[error("Replay error")]
216 ReplayError,
217 #[error("Configuration error: missing exchange")]
218 MissingExchange,
219 #[error("Configuration error: missing symbol")]
220 MissingSymbol,
221 #[error("Invalid session ID or signature")]
222 InvalidSessionId,
223}
224
225#[derive(Debug, Clone, Error, PartialEq, Eq, Hash, Copy, Serialize, Deserialize)]
227pub enum LoginError {
228 #[error("Username or password is empty")]
229 EmptyCredentials,
230 #[error("Username or password is invalid")]
231 InvalidCredentials,
232 #[error("OTP secret is empty")]
233 OTPSecretNotFound,
234 #[error("OTP secret is invalid")]
235 InvalidOTPSecret,
236 #[error("Wrong or expired session ID/signature")]
237 InvalidSession,
238 #[error("Session ID/signature is empty")]
239 SessionNotFound,
240 #[error("Cannot parse user ID")]
241 ParseIDError,
242 #[error("Cannot parse username")]
243 ParseUsernameError,
244 #[error("Cannot parse session hash")]
245 ParseSessionHashError,
246 #[error("Cannot parse private channel")]
247 ParsePrivateChannelError,
248 #[error("Cannot parse auth token")]
249 ParseAuthTokenError,
250 #[error("Missing auth token")]
251 MissingAuthToken,
252}