questdb/
error.rs

1/*******************************************************************************
2 *     ___                  _   ____  ____
3 *    / _ \ _   _  ___  ___| |_|  _ \| __ )
4 *   | | | | | | |/ _ \/ __| __| | | |  _ \
5 *   | |_| | |_| |  __/\__ \ |_| |_| | |_) |
6 *    \__\_\\__,_|\___||___/\__|____/|____/
7 *
8 *  Copyright (c) 2014-2019 Appsicle
9 *  Copyright (c) 2019-2025 QuestDB
10 *
11 *  Licensed under the Apache License, Version 2.0 (the "License");
12 *  you may not use this file except in compliance with the License.
13 *  You may obtain a copy of the License at
14 *
15 *  http://www.apache.org/licenses/LICENSE-2.0
16 *
17 *  Unless required by applicable law or agreed to in writing, software
18 *  distributed under the License is distributed on an "AS IS" BASIS,
19 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 *  See the License for the specific language governing permissions and
21 *  limitations under the License.
22 *
23 ******************************************************************************/
24use std::convert::Infallible;
25use std::fmt::{Display, Formatter};
26
27macro_rules! fmt {
28    ($code:ident, $($arg:tt)*) => {
29        crate::error::Error::new(
30            crate::error::ErrorCode::$code,
31            format!($($arg)*))
32    }
33}
34
35/// Category of error.
36///
37/// Accessible via Error's [`code`](Error::code) method.
38#[derive(Debug, Copy, Clone, PartialEq)]
39pub enum ErrorCode {
40    /// The host, port, or interface was incorrect.
41    CouldNotResolveAddr,
42
43    /// Called methods in the wrong order. E.g. `symbol` after `column`.
44    InvalidApiCall,
45
46    /// A network error connecting or flushing data out.
47    SocketError,
48
49    /// The string or symbol field is not encoded in valid UTF-8.
50    ///
51    /// *This error is reserved for the
52    /// [C and C++ API](https://github.com/questdb/c-questdb-client/).*
53    InvalidUtf8,
54
55    /// The table name or column name contains bad characters.
56    InvalidName,
57
58    /// The supplied timestamp is invalid.
59    InvalidTimestamp,
60
61    /// Error during the authentication process.
62    AuthError,
63
64    /// Error during TLS handshake.
65    TlsError,
66
67    /// The server does not support ILP-over-HTTP.
68    HttpNotSupported,
69
70    /// Error sent back from the server during flush.
71    ServerFlushError,
72
73    /// Bad configuration.
74    ConfigError,
75
76    /// There was an error serializing an array.
77    ArrayError,
78
79    /// Validate protocol version error.
80    ProtocolVersionError,
81}
82
83/// An error that occurred when using QuestDB client library.
84#[derive(Debug, PartialEq)]
85pub struct Error {
86    code: ErrorCode,
87    msg: String,
88}
89
90impl Error {
91    /// Create an error with the given code and message.
92    pub fn new<S: Into<String>>(code: ErrorCode, msg: S) -> Error {
93        Error {
94            code,
95            msg: msg.into(),
96        }
97    }
98
99    #[cfg(feature = "sync-sender-http")]
100    pub(crate) fn from_ureq_error(err: ureq::Error, url: &str) -> Error {
101        match err {
102            ureq::Error::StatusCode(code) => {
103                if code == 404 {
104                    fmt!(
105                        HttpNotSupported,
106                        "Could not flush buffer: HTTP endpoint does not support ILP."
107                    )
108                } else if [401, 403].contains(&code) {
109                    fmt!(
110                        AuthError,
111                        "Could not flush buffer: HTTP endpoint authentication error [code: {}]",
112                        code
113                    )
114                } else {
115                    fmt!(SocketError, "Could not flush buffer: {}: {}", url, err)
116                }
117            }
118            e => {
119                fmt!(SocketError, "Could not flush buffer: {}: {}", url, e)
120            }
121        }
122    }
123
124    /// Get the error code (category) of this error.
125    pub fn code(&self) -> ErrorCode {
126        self.code
127    }
128
129    /// Get the string message of this error.
130    pub fn msg(&self) -> &str {
131        &self.msg
132    }
133}
134
135impl Display for Error {
136    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
137        f.write_str(&self.msg)
138    }
139}
140
141impl std::error::Error for Error {}
142
143impl From<Infallible> for Error {
144    fn from(_: Infallible) -> Self {
145        unreachable!()
146    }
147}
148
149/// A specialized `Result` type for the crate's [`Error`] type.
150pub type Result<T> = std::result::Result<T, Error>;
151
152pub(crate) use fmt;