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}