surrealdb_common/error/
mod.rs1use core::fmt;
2use std::fmt::{Debug, Display};
3use std::ops::{Deref, DerefMut};
4use std::ptr::NonNull;
5use std::result::Result as StdResult;
6
7mod code;
8mod engine;
9mod leaf;
10mod raw;
11pub mod source;
12
13pub use code::ErrorCode;
14pub use engine::EngineError;
15pub use leaf::{LeafError, internal_todo};
16use raw::{RawError, RawTypedError};
17
18pub trait ErrorTrait: Display + Debug + 'static {
19 fn error_code(&self) -> ErrorCode {
20 ErrorCode::default()
21 }
22}
23
24impl<E: std::error::Error + 'static> ErrorTrait for E {}
25
26pub type Result<T, E = Error> = StdResult<T, E>;
27
28pub struct Error(RawError);
32
33impl Error {
34 #[cold]
36 pub fn new<E>(e: E) -> Self
37 where
38 E: ErrorTrait,
39 {
40 Error(RawError::new(e))
41 }
42
43 pub fn error_code(&self) -> ErrorCode {
45 self.0.error_code()
46 }
47
48 pub fn downcast_ref<T: ErrorTrait>(&self) -> Option<&T> {
50 self.0.is::<T>().then(|| unsafe { self.0.unchecked_ref() })
51 }
52
53 pub fn downcast_mut<T: ErrorTrait>(&mut self) -> Option<&mut T> {
55 self.0.is::<T>().then(|| unsafe { self.0.unchecked_mut() })
56 }
57
58 pub fn into_inner<T: ErrorTrait>(self) -> Result<T, Self> {
60 if self.0.is::<T>() {
61 Ok(unsafe { self.0.unchecked_into_inner() })
62 } else {
63 Err(self)
64 }
65 }
66
67 pub fn downcast<T: ErrorTrait>(self) -> Result<TypedError<T>, Self> {
69 if self.0.is::<T>() {
70 Ok(TypedError(unsafe { self.0.unchecked_cast() }))
71 } else {
72 Err(self)
73 }
74 }
75}
76
77impl fmt::Debug for Error {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 self.0.debug(f)
80 }
81}
82
83impl fmt::Display for Error {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 self.0.display(f)
86 }
87}
88
89pub struct TypedError<T: ErrorTrait>(RawTypedError<T>);
95
96impl<T: ErrorTrait> TypedError<T> {
97 pub fn new(e: T) -> Self {
99 TypedError(RawTypedError::new(e))
100 }
101
102 pub fn erase(self) -> Error {
104 Error(self.0.erase())
105 }
106
107 pub fn into_inner(self) -> T {
109 self.0.into_inner()
110 }
111
112 pub fn into_raw(self) -> NonNull<()> {
114 self.0.into_raw()
115 }
116
117 pub unsafe fn from_raw(ptr: NonNull<()>) -> Self {
123 unsafe { TypedError(RawTypedError::from_raw(ptr)) }
124 }
125
126 pub unsafe fn ref_from_raw<'a>(ptr: NonNull<()>) -> &'a T {
132 unsafe { RawTypedError::<T>::ref_from_raw(ptr) }
133 }
134
135 pub unsafe fn ref_mut_from_raw<'a>(ptr: NonNull<()>) -> &'a mut T {
141 unsafe { RawTypedError::<T>::ref_mut_from_raw(ptr) }
142 }
143}
144
145impl<T: ErrorTrait> Deref for TypedError<T> {
146 type Target = T;
147
148 fn deref(&self) -> &Self::Target {
149 self.0.deref()
150 }
151}
152
153impl<T: ErrorTrait> DerefMut for TypedError<T> {
154 fn deref_mut(&mut self) -> &mut Self::Target {
155 self.0.deref_mut()
156 }
157}
158
159impl<T: ErrorTrait> fmt::Debug for TypedError<T> {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 Debug::fmt(self.0.deref(), f)
162 }
163}
164
165impl<T: ErrorTrait> fmt::Display for TypedError<T> {
166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167 Display::fmt(self.0.deref(), f)
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use std::fmt;
174 use std::sync::atomic::{AtomicUsize, Ordering};
175
176 use super::{Error, TypedError};
177
178 const SENTINEL: u64 = 0xDEAD_BEEF_CAFE_BABE;
179
180 struct DropCounted {
188 value: Box<u64>,
189 counter: &'static AtomicUsize,
190 }
191
192 impl DropCounted {
193 fn new(counter: &'static AtomicUsize) -> Self {
194 DropCounted {
195 value: Box::new(SENTINEL),
196 counter,
197 }
198 }
199 }
200
201 impl fmt::Debug for DropCounted {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 write!(f, "DropCounted({:#x})", *self.value)
204 }
205 }
206
207 impl fmt::Display for DropCounted {
208 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
209 write!(f, "drop-counted error: {:#x}", *self.value)
210 }
211 }
212
213 impl std::error::Error for DropCounted {}
214
215 impl Drop for DropCounted {
216 fn drop(&mut self) {
217 self.counter.fetch_add(1, Ordering::SeqCst);
218 }
219 }
220
221 #[derive(Debug)]
223 struct OtherType;
224
225 impl fmt::Display for OtherType {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 f.write_str("OtherType")
228 }
229 }
230
231 impl std::error::Error for OtherType {}
232
233 #[test]
234 fn error_new_format_then_drop_runs_once() {
235 static COUNTER: AtomicUsize = AtomicUsize::new(0);
236 {
237 let err = Error::new(DropCounted::new(&COUNTER));
238 let debug = format!("{:?}", err);
239 let display = format!("{}", err);
240 assert!(debug.contains("DropCounted"), "unexpected debug: {debug}");
241 assert!(display.contains("drop-counted error"), "unexpected display: {display}",);
242 assert_eq!(COUNTER.load(Ordering::SeqCst), 0, "value dropped early");
243 }
244 assert_eq!(COUNTER.load(Ordering::SeqCst), 1, "expected exactly one drop");
245 }
246
247 #[test]
248 fn error_downcast_then_into_inner_drops_once() {
249 static COUNTER: AtomicUsize = AtomicUsize::new(0);
250 {
251 let err = Error::new(DropCounted::new(&COUNTER));
252 let typed: TypedError<DropCounted> =
253 err.downcast::<DropCounted>().expect("downcast should succeed");
254 assert_eq!(*typed.value, SENTINEL);
255 let inner = typed.into_inner();
256 assert_eq!(*inner.value, SENTINEL);
257 }
258 assert_eq!(COUNTER.load(Ordering::SeqCst), 1, "expected exactly one drop");
259 }
260
261 #[test]
262 fn error_into_inner_drops_once() {
263 static COUNTER: AtomicUsize = AtomicUsize::new(0);
264 {
265 let err = Error::new(DropCounted::new(&COUNTER));
266 let inner = err.into_inner::<DropCounted>().expect("into_inner should succeed");
267 assert_eq!(*inner.value, SENTINEL);
268 }
269 assert_eq!(COUNTER.load(Ordering::SeqCst), 1, "expected exactly one drop");
270 }
271
272 #[test]
273 fn error_downcast_failure_preserves_original() {
274 static COUNTER: AtomicUsize = AtomicUsize::new(0);
275 {
276 let err = Error::new(DropCounted::new(&COUNTER));
277 let err =
278 err.downcast::<OtherType>().expect_err("downcast to unrelated type should fail");
279 let _ = format!("{:?}", err);
280 let _ = format!("{}", err);
281 assert_eq!(COUNTER.load(Ordering::SeqCst), 0, "value dropped during failed downcast",);
282 }
283 assert_eq!(COUNTER.load(Ordering::SeqCst), 1, "expected exactly one drop");
284 }
285
286 #[test]
287 fn typed_error_erase_then_drop_runs_once() {
288 static COUNTER: AtomicUsize = AtomicUsize::new(0);
289 {
290 let typed = TypedError::new(DropCounted::new(&COUNTER));
291 let err: Error = typed.erase();
292 let _ = format!("{:?}", err);
293 }
294 assert_eq!(COUNTER.load(Ordering::SeqCst), 1, "expected exactly one drop");
295 }
296
297 #[test]
298 fn typed_error_erase_then_downcast_into_inner_drops_once() {
299 static COUNTER: AtomicUsize = AtomicUsize::new(0);
300 {
301 let typed = TypedError::new(DropCounted::new(&COUNTER));
302 let err = typed.erase();
303 let typed_back = err.downcast::<DropCounted>().expect("downcast should succeed");
304 let inner = typed_back.into_inner();
305 assert_eq!(*inner.value, SENTINEL);
306 }
307 assert_eq!(COUNTER.load(Ordering::SeqCst), 1, "expected exactly one drop");
308 }
309}