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