databend_client/
error.rs

1// Copyright 2021 Datafuse Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::error_code::ErrorCode;
16use reqwest::StatusCode;
17
18#[derive(Debug)]
19pub enum Error {
20    WithContext(Box<Error>, String),
21
22    /// errors detected before sending request.
23    /// e.g. invalid DSN, header value, stage name.
24    BadArgument(String),
25    /// errors when
26    /// 1. accessing local file and presign_url
27    /// 2. From(std::io::Error)
28    IO(String),
29
30    /// send request error
31    Request(String),
32
33    /// http handler return 200, but body is invalid
34    /// 1. failed to decode body to Utf8 or JSON
35    /// 2. failed to decode result data
36    Decode(String),
37
38    /// http handler return 200, but query failed (.error != null)
39    QueryFailed(ErrorCode),
40
41    /// http handler return non-200, with JSON body of type QueryError.
42    Logic(StatusCode, ErrorCode),
43
44    /// other non-200 response
45    Response {
46        status: StatusCode,
47        msg: String,
48    },
49
50    /// the flowing are more detail type of Logic
51    ///
52    /// possible reasons:
53    ///  - expired: if you have not polled the next_page_uri for too long, the session will be expired, you'll get a 404
54    ///    on accessing this next page uri.
55    ///  - routed to another server
56    ///  - server restarted
57    ///
58    /// TODO: try to distinguish them
59    QueryNotFound(String),
60    AuthFailure(ErrorCode),
61}
62
63impl Error {
64    pub fn response_error(status: StatusCode, body: &[u8]) -> Self {
65        Self::Response {
66            status,
67            msg: String::from_utf8_lossy(body).to_string(),
68        }
69    }
70
71    pub fn with_context(self, ctx: &str) -> Self {
72        Error::WithContext(Box::new(self), ctx.to_string())
73    }
74
75    pub fn status_code(&self) -> Option<StatusCode> {
76        match self {
77            Error::Logic(status, ..) => Some(*status),
78            Error::Response { status, .. } => Some(*status),
79            Error::WithContext(err, _) => err.status_code(),
80            _ => None,
81        }
82    }
83}
84
85impl std::fmt::Display for Error {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        match self {
88            Error::Decode(msg) => write!(f, "DecodeError: {msg}"),
89            Error::BadArgument(msg) => write!(f, "BadArgument: {msg}"),
90            Error::Request(msg) => write!(f, "{msg}"),
91            Error::Response { msg, status } => write!(f, "ResponseError: ({status}){msg}"),
92            Error::IO(msg) => write!(f, "IOError: {msg}"),
93            Error::Logic(status_code, ec) => write!(f, "BadRequest:({status_code}){ec}"),
94            Error::QueryNotFound(msg) => write!(f, "QueryNotFound: {msg}"),
95            Error::QueryFailed(ec) => write!(f, "QueryFailed: {ec}"),
96            Error::AuthFailure(ec) => write!(f, "AuthFailure: {ec}"),
97
98            Error::WithContext(err, ctx) => write!(f, "fail to {ctx}: {err}"),
99        }
100    }
101}
102
103impl std::error::Error for Error {}
104
105pub type Result<T, E = Error> = core::result::Result<T, E>;
106
107impl From<url::ParseError> for Error {
108    fn from(e: url::ParseError) -> Self {
109        Error::Decode(e.to_string())
110    }
111}
112
113impl From<std::num::ParseIntError> for Error {
114    fn from(e: std::num::ParseIntError) -> Self {
115        Error::Decode(e.to_string())
116    }
117}
118
119/// only used in make_headers
120impl From<reqwest::header::InvalidHeaderValue> for Error {
121    fn from(e: reqwest::header::InvalidHeaderValue) -> Self {
122        Error::BadArgument(e.to_string())
123    }
124}
125
126impl From<serde_json::Error> for Error {
127    fn from(e: serde_json::Error) -> Self {
128        Error::Decode(e.to_string())
129    }
130}
131
132impl From<reqwest::Error> for Error {
133    fn from(e: reqwest::Error) -> Self {
134        Error::Request(e.to_string())
135    }
136}
137
138impl From<std::io::Error> for Error {
139    fn from(e: std::io::Error) -> Self {
140        Error::IO(e.to_string())
141    }
142}
143
144impl From<std::str::Utf8Error> for Error {
145    fn from(e: std::str::Utf8Error) -> Self {
146        Error::Decode(e.to_string())
147    }
148}