Skip to main content

common_error/
ext.rs

1// Copyright 2023 Greptime Team
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 std::any::Any;
16use std::fmt::{Debug, Formatter};
17use std::sync::Arc;
18
19use snafu::{FromString, Snafu};
20
21use crate::status_code::StatusCode;
22
23/// Extension to [`Error`](std::error::Error) in std.
24pub trait ErrorExt: StackError {
25    /// Map this error to [StatusCode].
26    fn status_code(&self) -> StatusCode {
27        StatusCode::Unknown
28    }
29
30    /// Returns the error as [Any](std::any::Any) so that it can be
31    /// downcast to a specific implementation.
32    fn as_any(&self) -> &dyn Any;
33
34    fn output_msg(&self) -> String
35    where
36        Self: Sized,
37    {
38        match self.status_code() {
39            StatusCode::Unknown | StatusCode::Internal => {
40                // masks internal error from end user
41                format!("Internal error: {}", self.status_code() as u32)
42            }
43            _ => {
44                let error = self.last();
45                if let Some(external_error) = error.source() {
46                    let external_root = external_error.sources().last().unwrap();
47
48                    if error.transparent() {
49                        format!("{external_root}")
50                    } else {
51                        format!("{error}: {external_root}")
52                    }
53                } else {
54                    format!("{error}")
55                }
56            }
57        }
58    }
59
60    /// Find out root level error for nested error
61    fn root_cause(&self) -> Option<&dyn std::error::Error>
62    where
63        Self: Sized,
64    {
65        let error = self.last();
66        if let Some(external_error) = error.source() {
67            let external_root = external_error.sources().last().unwrap();
68            Some(external_root)
69        } else {
70            None
71        }
72    }
73}
74
75pub trait StackError: std::error::Error {
76    fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>);
77
78    fn next(&self) -> Option<&dyn StackError>;
79
80    fn last(&self) -> &dyn StackError
81    where
82        Self: Sized,
83    {
84        let Some(mut result) = self.next() else {
85            return self;
86        };
87        while let Some(err) = result.next() {
88            result = err;
89        }
90        result
91    }
92
93    /// Indicates whether this error is "transparent", that it delegates its "display" and "source"
94    /// to the underlying error. Could be useful when you are just wrapping some external error,
95    /// **AND** can not or would not provide meaningful contextual info. For example, the
96    /// `DataFusionError`.
97    fn transparent(&self) -> bool {
98        false
99    }
100}
101
102impl<T: ?Sized + StackError> StackError for Arc<T> {
103    fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
104        self.as_ref().debug_fmt(layer, buf)
105    }
106
107    fn next(&self) -> Option<&dyn StackError> {
108        self.as_ref().next()
109    }
110}
111
112impl<T: StackError> StackError for Box<T> {
113    fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
114        self.as_ref().debug_fmt(layer, buf)
115    }
116
117    fn next(&self) -> Option<&dyn StackError> {
118        self.as_ref().next()
119    }
120}
121
122/// A simple [Result] of which the error is convertible from [ErrorExt] (which every GreptimeDB
123/// error implements). Use this if you are tired of writing `unwrap`s in test codes, that you can
124/// use the `?` on all GreptimeDB errors.
125pub type WhateverResult<T> = Result<T, Whatever>;
126
127#[derive(Snafu)]
128#[snafu(display("{inner}"))]
129pub struct Whatever {
130    inner: snafu::Whatever,
131}
132
133impl Debug for Whatever {
134    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
135        write!(f, "{}", self.inner)
136    }
137}
138
139impl<E: ErrorExt> From<E> for Whatever {
140    fn from(e: E) -> Self {
141        Self {
142            inner: FromString::without_source(format!("{e:?}")),
143        }
144    }
145}
146
147impl From<String> for Whatever {
148    fn from(s: String) -> Self {
149        Self {
150            inner: FromString::without_source(s),
151        }
152    }
153}
154
155/// An opaque boxed error based on errors that implement [ErrorExt] trait.
156pub struct BoxedError {
157    inner: Box<dyn crate::ext::ErrorExt + Send + Sync>,
158}
159
160impl BoxedError {
161    pub fn new<E: crate::ext::ErrorExt + Send + Sync + 'static>(err: E) -> Self {
162        Self {
163            inner: Box::new(err),
164        }
165    }
166
167    pub fn into_inner(self) -> Box<dyn crate::ext::ErrorExt + Send + Sync> {
168        self.inner
169    }
170}
171
172impl std::fmt::Debug for BoxedError {
173    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
174        let mut buf = vec![];
175        self.debug_fmt(0, &mut buf);
176        write!(f, "{}", buf.join("\n"))
177    }
178}
179
180impl std::fmt::Display for BoxedError {
181    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182        write!(f, "{}", self.inner)
183    }
184}
185
186impl std::error::Error for BoxedError {
187    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
188        self.inner.source()
189    }
190}
191
192impl crate::ext::ErrorExt for BoxedError {
193    fn status_code(&self) -> crate::status_code::StatusCode {
194        self.inner.status_code()
195    }
196
197    fn as_any(&self) -> &dyn std::any::Any {
198        self.inner.as_any()
199    }
200}
201
202// Implement ErrorCompat for this opaque error so the backtrace is also available
203// via `ErrorCompat::backtrace()`.
204impl crate::snafu::ErrorCompat for BoxedError {
205    fn backtrace(&self) -> Option<&crate::snafu::Backtrace> {
206        None
207    }
208}
209
210impl StackError for BoxedError {
211    fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
212        self.inner.debug_fmt(layer, buf)
213    }
214
215    fn next(&self) -> Option<&dyn StackError> {
216        self.inner.next()
217    }
218}
219
220/// Error type with plain error message
221#[derive(Debug)]
222pub struct PlainError {
223    msg: String,
224    status_code: StatusCode,
225}
226
227impl PlainError {
228    pub fn new(msg: String, status_code: StatusCode) -> Self {
229        Self { msg, status_code }
230    }
231}
232
233impl std::fmt::Display for PlainError {
234    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235        write!(f, "{}", self.msg)
236    }
237}
238
239impl std::error::Error for PlainError {
240    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
241        None
242    }
243}
244
245impl crate::ext::ErrorExt for PlainError {
246    fn status_code(&self) -> crate::status_code::StatusCode {
247        self.status_code
248    }
249
250    fn as_any(&self) -> &dyn std::any::Any {
251        self as _
252    }
253}
254
255impl StackError for PlainError {
256    fn debug_fmt(&self, layer: usize, buf: &mut Vec<String>) {
257        buf.push(format!("{}: {}", layer, self.msg))
258    }
259
260    fn next(&self) -> Option<&dyn StackError> {
261        None
262    }
263}