Skip to main content

tdbe/
error.rs

1//! Encoding-layer errors for `ThetaData` Binary Encoding, plus the
2//! `ThetaData` HTTP error-code lookup table consumed by the networking
3//! crate when folding `tonic::Status` into a `thetadatadx::Error`.
4
5use thiserror::Error;
6
7/// Encoding-layer errors for `ThetaData` Binary Encoding.
8#[derive(Error, Debug)]
9pub enum Error {
10    /// FIT nibble decoding failure (malformed input, unexpected terminator).
11    #[error("FIT decode error: {0}")]
12    Decode(String),
13
14    /// FIE nibble encoding failure (invalid character).
15    #[error("FIE encode error: {0}")]
16    Encode(String),
17
18    /// Value conversion error (e.g., enum from invalid discriminant).
19    #[error("conversion error: {0}")]
20    Conversion(String),
21
22    /// Configuration / input validation error (e.g., unrecognised `right`
23    /// string supplied to [`crate::right::parse_right`]).
24    #[error("Configuration error: {0}")]
25    Config(String),
26
27    /// I/O error during read/write operations.
28    #[error("IO error: {0}")]
29    Io(#[from] std::io::Error),
30}
31
32/// Convenience Result type.
33pub type Result<T> = std::result::Result<T, Error>;
34
35// ─── ThetaData HTTP error-code lookup ───────────────────────────────────
36
37/// A `ThetaData` error code with its HTTP status, short name, and description.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub struct ThetaDataError {
40    pub http_code: u16,
41    pub name: &'static str,
42    pub description: &'static str,
43}
44
45const ERRORS: &[ThetaDataError] = &[
46    ThetaDataError {
47        http_code: 200,
48        name: "OK",
49        description: "Request completed successfully.",
50    },
51    ThetaDataError {
52        http_code: 404,
53        name: "NO_IMPL",
54        description: "Endpoint or feature is not implemented.",
55    },
56    ThetaDataError {
57        http_code: 429,
58        name: "OS_LIMIT",
59        description: "Rate limit exceeded for the current subscription tier.",
60    },
61    ThetaDataError {
62        http_code: 470,
63        name: "GENERAL",
64        description: "General server-side error.",
65    },
66    ThetaDataError {
67        http_code: 471,
68        name: "PERMISSION",
69        description: "Insufficient permissions for the requested data.",
70    },
71    ThetaDataError {
72        http_code: 472,
73        name: "NO_DATA",
74        description: "No data available for the requested parameters.",
75    },
76    ThetaDataError {
77        http_code: 473,
78        name: "INVALID_PARAMS",
79        description: "One or more request parameters are invalid.",
80    },
81    ThetaDataError {
82        http_code: 474,
83        name: "DISCONNECTED",
84        description: "Client is disconnected from the server.",
85    },
86    ThetaDataError {
87        http_code: 475,
88        name: "TERMINAL_PARSE",
89        description: "Server failed to parse the terminal request.",
90    },
91    ThetaDataError {
92        http_code: 476,
93        name: "WRONG_IP",
94        description: "Request originated from an unauthorized IP address.",
95    },
96    ThetaDataError {
97        http_code: 477,
98        name: "NO_PAGE_FOUND",
99        description: "The requested page was not found.",
100    },
101    ThetaDataError {
102        http_code: 478,
103        name: "INVALID_SESSION_ID",
104        description: "The session ID is invalid or expired.",
105    },
106    ThetaDataError {
107        http_code: 571,
108        name: "SERVER_STARTING",
109        description: "Server is still starting up; retry shortly.",
110    },
111    ThetaDataError {
112        http_code: 572,
113        name: "UNCAUGHT_ERROR",
114        description: "An uncaught server-side error occurred.",
115    },
116];
117
118/// Look up a `ThetaDataError` by its HTTP status code.
119#[inline]
120#[must_use]
121pub fn error_from_http_code(code: u16) -> Option<&'static ThetaDataError> {
122    ERRORS.iter().find(|e| e.http_code == code)
123}
124
125/// Metadata key carrying the ThetaData HTTP status in gRPC responses.
126pub const HTTP_STATUS_CODE_KEY: &str = "http_status_code";
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn known_codes() {
134        assert_eq!(error_from_http_code(200).unwrap().name, "OK");
135        assert_eq!(error_from_http_code(472).unwrap().name, "NO_DATA");
136        assert_eq!(error_from_http_code(571).unwrap().name, "SERVER_STARTING");
137        assert_eq!(error_from_http_code(572).unwrap().name, "UNCAUGHT_ERROR");
138    }
139
140    #[test]
141    fn unknown_code() {
142        assert!(error_from_http_code(999).is_none());
143        assert!(error_from_http_code(500).is_none());
144    }
145}