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    /// The supplied decimal is invalid.
83    InvalidDecimal,
84}
85
86/// An error that occurred when using QuestDB client library.
87#[derive(Debug, PartialEq)]
88pub struct Error {
89    code: ErrorCode,
90    msg: String,
91}
92
93impl Error {
94    /// Create an error with the given code and message.
95    pub fn new<S: Into<String>>(code: ErrorCode, msg: S) -> Error {
96        Error {
97            code,
98            msg: msg.into(),
99        }
100    }
101
102    #[cfg(feature = "sync-sender-http")]
103    pub(crate) fn from_ureq_error(err: ureq::Error, url: &str) -> Error {
104        match err {
105            ureq::Error::StatusCode(code) => {
106                if code == 404 {
107                    fmt!(
108                        HttpNotSupported,
109                        "Could not flush buffer: HTTP endpoint does not support ILP."
110                    )
111                } else if [401, 403].contains(&code) {
112                    fmt!(
113                        AuthError,
114                        "Could not flush buffer: HTTP endpoint authentication error [code: {}]",
115                        code
116                    )
117                } else {
118                    fmt!(SocketError, "Could not flush buffer: {}: {}", url, err)
119                }
120            }
121            e => {
122                fmt!(SocketError, "Could not flush buffer: {}: {}", url, e)
123            }
124        }
125    }
126
127    /// Get the error code (category) of this error.
128    pub fn code(&self) -> ErrorCode {
129        self.code
130    }
131
132    /// Get the string message of this error.
133    pub fn msg(&self) -> &str {
134        &self.msg
135    }
136}
137
138impl Display for Error {
139    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
140        f.write_str(&self.msg)
141    }
142}
143
144impl std::error::Error for Error {}
145
146impl From<Infallible> for Error {
147    fn from(_: Infallible) -> Self {
148        unreachable!()
149    }
150}
151
152/// A specialized `Result` type for the crate's [`Error`] type.
153pub type Result<T> = std::result::Result<T, Error>;
154
155pub(crate) use fmt;