1use std::any::{Any, TypeId};
2use std::borrow::Cow;
3use std::collections::HashMap;
4use std::error::Error as StdError;
5use std::fmt;
6use std::str;
7
8use crate::kinds::UserError;
9use crate::kinds::{error_name, tag_check};
10use crate::traits::{ErrorKind, Field};
11
12const FIELD_HINT: u16 = 0x_00_01;
13const FIELD_DETAILS: u16 = 0x_00_02;
14const FIELD_SERVER_TRACEBACK: u16 = 0x_01_01;
15
16const FIELD_POSITION_START: u16 = 0x_FF_F1;
18const FIELD_POSITION_END: u16 = 0x_FF_F2;
19const FIELD_LINE: u16 = 0x_FF_F3;
20const FIELD_COLUMN: u16 = 0x_FF_F4;
21
22#[derive(Debug)]
26pub struct Error(pub(crate) Box<Inner>);
27
28pub struct Chain<'a>(Option<&'a (dyn StdError + 'static)>);
29
30#[derive(Clone, Copy)]
32pub struct Tag {
33    pub(crate) bit: u32,
34}
35
36pub(crate) enum Source {
37    Box(Box<dyn StdError + Send + Sync + 'static>),
38    Ref(Box<dyn AsRef<dyn StdError + Send + Sync + 'static> + Send + Sync + 'static>),
39}
40
41#[derive(Debug)]
42pub(crate) struct Inner {
43    pub code: u32,
44    pub messages: Vec<Cow<'static, str>>,
46    pub error: Option<Source>,
47    pub headers: HashMap<u16, bytes::Bytes>,
49    pub annotations: HashMap<String, String>,
50    pub fields: HashMap<(&'static str, TypeId), Box<dyn Any + Send + Sync>>,
51}
52
53impl Error {
54    pub fn is<T: ErrorKind>(&self) -> bool {
55        T::is_superclass_of(self.0.code)
56    }
57    pub fn has_tag(&self, tag: Tag) -> bool {
58        tag_check(self.0.code, tag.bit)
59    }
60    pub fn chain(&self) -> Chain {
61        Chain(Some(self))
62    }
63    pub fn context<S: Into<Cow<'static, str>>>(mut self, msg: S) -> Error {
64        self.0.messages.push(msg.into());
65        self
66    }
67    pub fn headers(&self) -> &HashMap<u16, bytes::Bytes> {
68        &self.0.headers
69    }
70    pub fn with_headers(mut self, headers: HashMap<u16, bytes::Bytes>) -> Error {
71        self.0.headers = headers;
72        self
73    }
74    pub fn annotations(&self) -> &HashMap<String, String> {
75        &self.0.annotations
76    }
77    pub fn with_annotations(mut self, annotations: HashMap<String, String>) -> Error {
78        self.0.annotations = annotations;
79        self
80    }
81    pub fn kind_name(&self) -> &str {
82        error_name(self.0.code)
83    }
84    pub fn kind_debug(&self) -> impl fmt::Display {
85        format!("{} [0x{:08X}]", error_name(self.0.code), self.0.code)
86    }
87    pub fn initial_message(&self) -> Option<&str> {
88        self.0.messages.first().map(|m| &m[..])
89    }
90    pub fn contexts(&self) -> impl DoubleEndedIterator<Item = &str> {
91        self.0.messages.iter().skip(1).map(|m| &m[..])
92    }
93    fn header(&self, field: u16) -> Option<&str> {
94        if let Some(value) = self.headers().get(&field) {
95            if let Ok(value) = str::from_utf8(value) {
96                return Some(value);
97            }
98        }
99        None
100    }
101    fn usize_header(&self, field: u16) -> Option<usize> {
102        self.header(field)
103            .and_then(|x| x.parse::<u32>().ok())
104            .map(|x| x as usize)
105    }
106    pub fn hint(&self) -> Option<&str> {
107        self.header(FIELD_HINT)
108    }
109    pub fn details(&self) -> Option<&str> {
110        self.header(FIELD_DETAILS)
111    }
112    pub fn server_traceback(&self) -> Option<&str> {
113        self.header(FIELD_SERVER_TRACEBACK)
114    }
115    pub fn position_start(&self) -> Option<usize> {
116        self.usize_header(FIELD_POSITION_START)
117    }
118    pub fn position_end(&self) -> Option<usize> {
119        self.usize_header(FIELD_POSITION_END)
120    }
121    pub fn line(&self) -> Option<usize> {
122        self.usize_header(FIELD_LINE)
123    }
124    pub fn column(&self) -> Option<usize> {
125        self.usize_header(FIELD_COLUMN)
126    }
127    pub(crate) fn unknown_headers(&self) -> impl Iterator<Item = (&u16, &bytes::Bytes)> {
128        self.headers().iter().filter(|(key, _)| {
129            **key != FIELD_HINT
130                && **key != FIELD_DETAILS
131                && **key != FIELD_POSITION_START
132                && **key != FIELD_POSITION_END
133                && **key != FIELD_LINE
134                && **key != FIELD_COLUMN
135        })
136    }
137    pub fn from_code(code: u32) -> Error {
138        Error(Box::new(Inner {
139            code,
140            messages: Vec::new(),
141            error: None,
142            headers: HashMap::new(),
143            annotations: HashMap::new(),
144            fields: HashMap::new(),
145        }))
146    }
147    pub fn code(&self) -> u32 {
148        self.0.code
149    }
150    pub fn refine_kind<T: ErrorKind>(mut self) -> Error {
151        self.0.code = T::CODE;
152        self
153    }
154    pub fn set<T: Field>(mut self, value: impl Into<T::Value>) -> Error {
155        self.0
156            .fields
157            .insert((T::NAME, TypeId::of::<T::Value>()), Box::new(value.into()));
158        self
159    }
160    pub fn get<T: Field>(&self) -> Option<&T::Value> {
161        self.0
162            .fields
163            .get(&(T::NAME, TypeId::of::<T::Value>()))
164            .and_then(|bx| bx.downcast_ref::<T::Value>())
165    }
166}
167
168impl fmt::Display for Error {
169    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170        let kind = self.kind_name();
171        if f.alternate() {
172            write!(f, "{kind}")?;
173            for msg in self.0.messages.iter().rev() {
174                write!(f, ": {msg}")?;
175            }
176            if let Some(mut src) = self.source() {
177                write!(f, ": {src}")?;
178                while let Some(next) = src.source() {
179                    write!(f, ": {next}")?;
180                    src = next;
181                }
182            }
183        } else if let Some(last) = self.0.messages.last() {
184            write!(f, "{kind}: {last}")?;
185        } else {
186            write!(f, "{kind}")?;
187        }
188        if let Some((line, col)) = self.line().zip(self.column()) {
189            write!(f, " (on line {line}, column {col})")?;
190        }
191        if let Some(hint) = self.hint() {
192            write!(f, "\n  Hint: {hint}")?;
193        }
194        if let Some(detail) = self.details() {
195            write!(f, "\n  Detail: {detail}")?;
196        }
197        Ok(())
198    }
199}
200
201impl StdError for Error {
202    fn source(&self) -> Option<&(dyn StdError + 'static)> {
203        self.0.error.as_ref().map(|s| match s {
204            Source::Box(b) => b.as_ref() as &dyn std::error::Error,
205            Source::Ref(b) => (**b).as_ref() as &dyn std::error::Error,
206        })
207    }
208}
209
210impl fmt::Debug for Source {
211    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212        match self {
213            Source::Box(b) => fmt::Debug::fmt(b.as_ref(), f),
214            Source::Ref(b) => fmt::Debug::fmt((**b).as_ref(), f),
215        }
216    }
217}
218
219impl<T> From<T> for Error
220where
221    T: AsRef<dyn StdError + Send + Sync + 'static> + Send + Sync + 'static,
222{
223    fn from(err: T) -> Error {
224        UserError::with_source_ref(err)
225    }
226}
227
228impl<'a> Iterator for Chain<'a> {
229    type Item = &'a (dyn StdError + 'static);
230    fn next(&mut self) -> Option<Self::Item> {
231        let result = self.0.take();
232        self.0 = result.and_then(|e| e.source());
233        result
234    }
235}