1#[cfg(not(feature = "std"))]
4use alloc::{
5 borrow::Cow,
6 boxed::Box,
7 format,
8 string::{String, ToString},
9 vec,
10 vec::Vec,
11};
12
13#[cfg(feature = "std")]
14use std::borrow::Cow;
15#[cfg(feature = "std")]
16use std::sync::OnceLock;
17
18use core::fmt;
19
20#[cfg(feature = "std")]
21use std::error::Error as StdError;
22
23#[derive(Debug)]
51pub struct Handled<E = Error> {
52 pub(crate) source: E,
53 pub(crate) message: OnceLock<String>,
56 pub(crate) locations: LocationVec,
59 pub(crate) contexts: Option<Vec<ContextEntry>>,
62 #[cfg(feature = "std")]
65 pub(crate) chained: Option<Box<Handled<Error>>>,
66}
67
68#[cfg(feature = "std")]
74#[derive(Debug)]
75pub struct Error(Box<dyn StdError + Send + Sync + 'static>);
76
77#[cfg(not(feature = "std"))]
78#[derive(Debug)]
79pub struct Error(Box<dyn fmt::Debug + fmt::Display + Send + Sync + 'static>);
80
81#[cfg(feature = "std")]
82impl Error {
83 #[inline]
85 pub fn new<E: StdError + Send + Sync + 'static>(e: E) -> Self {
86 Self(Box::new(e))
87 }
88
89 #[inline]
91 pub fn from_box(e: Box<dyn StdError + Send + Sync + 'static>) -> Self {
92 Self(e)
93 }
94
95 #[inline]
97 pub fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static) {
98 self.0.as_ref()
99 }
100
101 #[inline]
103 pub fn as_dyn_error(&self) -> &(dyn StdError + 'static) {
104 self.0.as_ref()
105 }
106
107 #[inline]
109 pub fn downcast_ref<T: StdError + 'static>(&self) -> Option<&T> {
110 self.0.downcast_ref::<T>()
111 }
112
113 #[inline]
115 pub fn downcast<T: StdError + 'static>(self) -> core::result::Result<T, Self> {
116 match self.0.downcast::<T>() {
117 Ok(e) => Ok(*e),
118 Err(e) => Err(Self(e)),
119 }
120 }
121
122 pub fn into_inner(self) -> Box<dyn StdError + Send + Sync + 'static> {
124 self.0
125 }
126}
127
128#[cfg(not(feature = "std"))]
129impl Error {
130 #[inline]
132 pub fn new<E: fmt::Debug + fmt::Display + Send + Sync + 'static>(e: E) -> Self {
133 Self(Box::new(e))
134 }
135
136 #[inline]
138 pub fn from_box(e: Box<dyn fmt::Debug + fmt::Display + Send + Sync + 'static>) -> Self {
139 Self(e)
140 }
141}
142
143impl fmt::Display for Error {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 self.0.fmt(f)
146 }
147}
148
149#[cfg(feature = "std")]
152impl<E: StdError + Send + Sync + 'static> From<E> for Error {
153 fn from(e: E) -> Self {
154 Error::new(e)
155 }
156}
157
158#[derive(Debug, Clone, Copy)]
160pub(crate) struct Location {
161 pub(crate) file: &'static str, pub(crate) line: u32,
163 pub(crate) col: u32,
164}
165
166const INLINE_CAPACITY: usize = 4;
169
170#[derive(Debug)]
171pub(crate) struct LocationVec {
172 len: u8,
173 inline: [core::mem::MaybeUninit<Location>; INLINE_CAPACITY],
174 overflow: Option<Vec<Location>>,
175}
176
177impl Clone for LocationVec {
178 fn clone(&self) -> Self {
179 let mut new = Self::new();
180 for loc in self.iter() {
181 new.push(*loc);
182 }
183 new
184 }
185}
186
187impl LocationVec {
188 #[inline]
189 pub const fn new() -> Self {
190 Self {
191 len: 0,
192 inline: [core::mem::MaybeUninit::uninit(); INLINE_CAPACITY],
193 overflow: None,
194 }
195 }
196
197 #[inline]
198 pub fn push(&mut self, loc: Location) {
199 let idx = self.len as usize;
200 if idx < INLINE_CAPACITY {
201 self.inline[idx] = core::mem::MaybeUninit::new(loc);
202 self.len += 1;
203 } else if idx < DEFAULT_LOCATION_LIMIT {
204 let overflow = self.overflow.get_or_insert_with(Vec::new);
206 overflow.push(loc);
207 self.len += 1;
208 }
209 }
211
212 #[inline]
213 pub fn len(&self) -> usize {
214 self.len as usize
215 }
216
217 #[inline]
218 pub fn is_empty(&self) -> bool {
219 self.len == 0
220 }
221
222 #[inline]
223 pub fn iter(&self) -> impl Iterator<Item = &Location> + '_ {
224 let inline_count = core::cmp::min(self.len as usize, INLINE_CAPACITY);
225 let inline_iter = (0..inline_count).map(move |i| {
226 unsafe { self.inline[i].assume_init_ref() }
228 });
229 let overflow_iter = self.overflow.iter().flat_map(|v| v.iter());
230 inline_iter.chain(overflow_iter)
231 }
232}
233
234#[derive(Debug, Clone)]
236pub(crate) struct ContextEntry {
237 pub(crate) location_idx: u16, pub(crate) message: Option<String>,
239 pub(crate) attachments: Vec<(Cow<'static, str>, Value)>,
240}
241
242#[derive(Debug, Clone, PartialEq)]
246pub enum Value {
247 String(String),
249 Int(i64),
251 Uint(u64),
253 Float(f64),
255 Bool(bool),
257 Null,
259}
260
261impl Value {
262 pub fn from<T: IntoValue>(v: T) -> Self {
264 v.into_value()
265 }
266}
267
268impl fmt::Display for Value {
269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
270 match self {
271 Value::String(s) => write!(f, "{}", s),
272 Value::Int(n) => write!(f, "{}", n),
273 Value::Uint(n) => write!(f, "{}", n),
274 Value::Float(n) => write!(f, "{}", n),
275 Value::Bool(b) => write!(f, "{}", b),
276 Value::Null => write!(f, "null"),
277 }
278 }
279}
280
281impl PartialEq<str> for Value {
283 fn eq(&self, other: &str) -> bool {
284 match self {
285 Value::String(s) => s == other,
286 _ => false,
287 }
288 }
289}
290
291impl PartialEq<&str> for Value {
292 fn eq(&self, other: &&str) -> bool {
293 self == *other
294 }
295}
296
297impl PartialEq<String> for Value {
298 fn eq(&self, other: &String) -> bool {
299 self == other.as_str()
300 }
301}
302
303impl PartialEq<i64> for Value {
304 fn eq(&self, other: &i64) -> bool {
305 match self {
306 Value::Int(n) => n == other,
307 _ => false,
308 }
309 }
310}
311
312impl PartialEq<u64> for Value {
313 fn eq(&self, other: &u64) -> bool {
314 match self {
315 Value::Uint(n) => n == other,
316 _ => false,
317 }
318 }
319}
320
321impl PartialEq<f64> for Value {
322 fn eq(&self, other: &f64) -> bool {
323 match self {
324 Value::Float(n) => (n - other).abs() < f64::EPSILON,
325 _ => false,
326 }
327 }
328}
329
330impl PartialEq<bool> for Value {
331 fn eq(&self, other: &bool) -> bool {
332 match self {
333 Value::Bool(b) => b == other,
334 _ => false,
335 }
336 }
337}
338
339pub trait IntoValue {
341 fn into_value(self) -> Value;
342}
343
344impl IntoValue for Value {
345 fn into_value(self) -> Value {
346 self
347 }
348}
349
350impl IntoValue for String {
351 fn into_value(self) -> Value {
352 Value::String(self)
353 }
354}
355
356impl IntoValue for &str {
357 fn into_value(self) -> Value {
358 Value::String(self.to_string())
359 }
360}
361
362impl<'a> IntoValue for Cow<'a, str> {
363 fn into_value(self) -> Value {
364 Value::String(self.into_owned())
365 }
366}
367
368impl IntoValue for bool {
369 fn into_value(self) -> Value {
370 Value::Bool(self)
371 }
372}
373
374impl IntoValue for i8 {
375 fn into_value(self) -> Value {
376 Value::Int(self as i64)
377 }
378}
379
380impl IntoValue for i16 {
381 fn into_value(self) -> Value {
382 Value::Int(self as i64)
383 }
384}
385
386impl IntoValue for i32 {
387 fn into_value(self) -> Value {
388 Value::Int(self as i64)
389 }
390}
391
392impl IntoValue for i64 {
393 fn into_value(self) -> Value {
394 Value::Int(self)
395 }
396}
397
398impl IntoValue for isize {
399 fn into_value(self) -> Value {
400 Value::Int(self as i64)
401 }
402}
403
404impl IntoValue for u8 {
405 fn into_value(self) -> Value {
406 Value::Uint(self as u64)
407 }
408}
409
410impl IntoValue for u16 {
411 fn into_value(self) -> Value {
412 Value::Uint(self as u64)
413 }
414}
415
416impl IntoValue for u32 {
417 fn into_value(self) -> Value {
418 Value::Uint(self as u64)
419 }
420}
421
422impl IntoValue for u64 {
423 fn into_value(self) -> Value {
424 Value::Uint(self)
425 }
426}
427
428impl IntoValue for usize {
429 fn into_value(self) -> Value {
430 Value::Uint(self as u64)
431 }
432}
433
434impl IntoValue for f32 {
435 fn into_value(self) -> Value {
436 Value::Float(self as f64)
437 }
438}
439
440impl IntoValue for f64 {
441 fn into_value(self) -> Value {
442 Value::Float(self)
443 }
444}
445
446impl<T: IntoValue> IntoValue for Option<T> {
447 fn into_value(self) -> Value {
448 match self {
449 Some(v) => v.into_value(),
450 None => Value::Null,
451 }
452 }
453}
454
455impl<T: IntoValue + Clone> IntoValue for &T {
457 fn into_value(self) -> Value {
458 self.clone().into_value()
459 }
460}
461
462pub const DEFAULT_LOCATION_LIMIT: usize = 32;
464pub const DEFAULT_CONTEXT_LIMIT: usize = 8;
465
466#[derive(Debug, Clone)]
468pub struct FrameView<'a> {
469 pub file: &'a str,
471 pub line: u32,
473 pub col: u32,
475 pub context: Option<&'a str>,
477 attachments_inner: &'a [(Cow<'static, str>, Value)],
479}
480
481impl<'a> FrameView<'a> {
482 pub fn attachments(&self) -> impl Iterator<Item = (&'a str, &'a Value)> {
484 self.attachments_inner.iter().map(|(k, v)| (k.as_ref(), v))
485 }
486
487 pub fn attachments_str(&self) -> impl Iterator<Item = (&'a str, String)> + 'a {
489 self.attachments_inner.iter().map(|(k, v)| (k.as_ref(), v.to_string()))
490 }
491}
492
493#[doc(hidden)]
503pub trait TryCatch<Target> {
504 fn try_catch(&self) -> Option<&Target>;
505}
506
507impl<T> TryCatch<T> for T {
509 #[inline]
510 fn try_catch(&self) -> Option<&T> {
511 Some(self)
512 }
513}
514
515#[cfg(feature = "std")]
517impl<T: StdError + 'static> TryCatch<T> for Error {
518 #[inline]
519 fn try_catch(&self) -> Option<&T> {
520 self.downcast_ref::<T>()
521 }
522}
523
524#[derive(Debug)]
529pub struct StringError(pub(crate) String);
530
531impl fmt::Display for StringError {
532 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
533 f.write_str(&self.0)
534 }
535}
536
537#[cfg(feature = "std")]
538impl StdError for StringError {}
539
540impl<E> Handled<E> {
545 #[cfg(feature = "std")]
548 #[inline]
549 pub fn new(source: E) -> Self
550 where
551 E: fmt::Display,
552 {
553 Self {
554 source,
555 message: OnceLock::new(),
556 locations: LocationVec::new(),
557 contexts: None,
558 chained: None,
559 }
560 }
561
562 #[cfg(not(feature = "std"))]
563 #[inline]
564 pub fn new(source: E) -> Self
565 where
566 E: fmt::Display,
567 {
568 Self {
569 source,
570 message: OnceLock::new(),
571 locations: LocationVec::new(),
572 contexts: None,
573 }
574 }
575
576 #[doc(hidden)]
579 #[inline]
580 pub fn frame(mut self, file: &'static str, line: u32, col: u32) -> Self {
581 if self.locations.len() < DEFAULT_LOCATION_LIMIT {
582 self.locations.push(Location { file, line, col });
583 }
584 self
585 }
586
587 #[doc(hidden)]
590 #[inline]
591 pub fn ctx(mut self, msg: impl Into<String>) -> Self {
592 let location_idx = self.locations.len().saturating_sub(1) as u16;
593 let contexts = self.contexts.get_or_insert_with(Vec::new);
594
595 if contexts.len() < DEFAULT_CONTEXT_LIMIT {
596 if let Some(entry) = contexts.iter_mut().find(|e| e.location_idx == location_idx) {
598 entry.message = Some(msg.into());
599 } else {
600 contexts.push(ContextEntry {
601 location_idx,
602 message: Some(msg.into()),
603 attachments: Vec::new(),
604 });
605 }
606 }
607 self
608 }
609
610 #[doc(hidden)]
613 #[inline]
614 pub fn scope(
615 mut self,
616 file: &'static str,
617 line: u32,
618 col: u32,
619 msg: impl Into<String>,
620 ) -> Self {
621 if self.locations.len() < DEFAULT_LOCATION_LIMIT {
622 self.locations.push(Location { file, line, col });
623 let location_idx = (self.locations.len() - 1) as u16;
624
625 let contexts = self.contexts.get_or_insert_with(Vec::new);
626 if contexts.len() < DEFAULT_CONTEXT_LIMIT {
627 contexts.push(ContextEntry {
628 location_idx,
629 message: Some(msg.into()),
630 attachments: Vec::new(),
631 });
632 }
633 }
634 self
635 }
636
637 #[doc(hidden)]
639 #[inline]
640 pub fn scope_kv(
641 mut self,
642 file: &'static str,
643 line: u32,
644 col: u32,
645 msg: impl Into<String>,
646 attachments: Vec<(Cow<'static, str>, Value)>,
647 ) -> Self {
648 if self.locations.len() < DEFAULT_LOCATION_LIMIT {
649 self.locations.push(Location { file, line, col });
650 let location_idx = (self.locations.len() - 1) as u16;
651
652 let contexts = self.contexts.get_or_insert_with(Vec::new);
653 if contexts.len() < DEFAULT_CONTEXT_LIMIT {
654 contexts.push(ContextEntry {
655 location_idx,
656 message: Some(msg.into()),
657 attachments,
658 });
659 }
660 }
661 self
662 }
663
664 #[doc(hidden)]
666 #[inline]
667 pub fn kv(mut self, key: &'static str, val: impl IntoValue) -> Self {
668 let location_idx = self.locations.len().saturating_sub(1) as u16;
669 let contexts = self.contexts.get_or_insert_with(Vec::new);
670
671 if let Some(entry) = contexts.iter_mut().find(|e| e.location_idx == location_idx) {
673 entry.attachments.push((Cow::Borrowed(key), val.into_value()));
674 } else if contexts.len() < DEFAULT_CONTEXT_LIMIT {
675 contexts.push(ContextEntry {
676 location_idx,
677 message: None,
678 attachments: vec![(Cow::Borrowed(key), val.into_value())],
679 });
680 }
681 self
682 }
683
684 pub fn message(&self) -> &str
686 where
687 E: fmt::Display,
688 {
689 self.message.get_or_init(|| self.source.to_string())
690 }
691
692 #[doc(hidden)]
697 #[inline]
698 pub fn try_catch<Target>(&self) -> Option<&Target>
699 where
700 E: TryCatch<Target>,
701 {
702 self.source.try_catch()
703 }
704
705 pub fn source_ref(&self) -> &E {
707 &self.source
708 }
709
710 pub fn into_source(self) -> E {
712 self.source
713 }
714
715 pub fn frames(&self) -> impl Iterator<Item = FrameView<'_>> {
718 let contexts = self.contexts.as_ref();
719 self.locations.iter().enumerate().map(move |(idx, loc)| {
720 let idx = idx as u16;
721 let ctx = contexts.and_then(|c| c.iter().find(|e| e.location_idx == idx));
722 FrameView {
723 file: loc.file,
724 line: loc.line,
725 col: loc.col,
726 context: ctx.and_then(|c| c.message.as_deref()),
727 attachments_inner: ctx.map(|c| c.attachments.as_slice()).unwrap_or(&[]),
728 }
729 })
730 }
731
732 pub fn depth(&self) -> usize {
734 self.locations.len()
735 }
736
737 pub fn is_empty(&self) -> bool {
739 self.locations.is_empty()
740 }
741
742 pub fn context_count(&self) -> usize {
744 self.contexts.as_ref().map(|c| c.len()).unwrap_or(0)
745 }
746
747 #[track_caller]
749 pub fn here(self) -> Self {
750 let loc = core::panic::Location::caller();
751 self.frame(loc.file(), loc.line(), loc.column())
752 }
753
754 #[cfg(feature = "std")]
756 pub fn erase(self) -> Handled<Error>
757 where
758 E: StdError + Send + Sync + 'static,
759 {
760 Handled {
761 source: Error::new(self.source),
762 message: self.message,
763 locations: self.locations,
764 contexts: self.contexts,
765 chained: self.chained,
766 }
767 }
768
769 pub fn map_err<F, O>(self, f: F) -> Handled<O>
771 where
772 F: FnOnce(E) -> O,
773 O: fmt::Display,
774 {
775 let new_source = f(self.source);
776 Handled {
777 source: new_source,
778 message: OnceLock::new(), locations: self.locations,
780 contexts: self.contexts,
781 #[cfg(feature = "std")]
782 chained: self.chained,
783 }
784 }
785}
786
787impl Handled<Error> {
792 #[cfg(feature = "std")]
795 #[inline]
796 pub fn wrap<E>(e: E) -> Self
797 where
798 E: StdError + Send + Sync + 'static,
799 {
800 use core::any::TypeId;
801 if TypeId::of::<E>() == TypeId::of::<Self>() {
802 unsafe {
805 let handled = core::ptr::read(&e as *const E as *const Self);
806 core::mem::forget(e);
807 handled
808 }
809 } else {
810 Self {
811 source: Error::new(e),
812 message: OnceLock::new(),
813 locations: LocationVec::new(),
814 contexts: None,
815 chained: None,
816 }
817 }
818 }
819
820 #[cfg(feature = "std")]
824 #[inline]
825 pub fn wrap_box(e: Box<dyn StdError + Send + Sync + 'static>) -> Self {
826 match e.downcast::<Self>() {
828 Ok(handled) => *handled,
829 Err(e) => {
830 Self {
831 source: Error::from_box(e),
832 message: OnceLock::new(),
833 locations: LocationVec::new(),
834 contexts: None,
835 chained: None,
836 }
837 }
838 }
839 }
840
841 #[cfg(feature = "std")]
844 #[inline]
845 pub fn wrap_erased(e: Error) -> Self {
846 Self {
847 source: e,
848 message: OnceLock::new(),
849 locations: LocationVec::new(),
850 contexts: None,
851 chained: None,
852 }
853 }
854
855 #[doc(hidden)]
858 #[cfg(feature = "std")]
859 #[inline]
860 pub fn wrap_box_with_frame(
861 e: Box<dyn StdError + Send + Sync + 'static>,
862 file: &'static str,
863 line: u32,
864 col: u32,
865 ) -> Self {
866 Self::wrap_box(e).frame(file, line, col)
867 }
868
869 #[cfg(not(feature = "std"))]
870 #[inline]
871 pub fn wrap<E>(e: E) -> Self
872 where
873 E: fmt::Debug + fmt::Display + Send + Sync + 'static,
874 {
875 use core::any::TypeId;
876 if TypeId::of::<E>() == TypeId::of::<Self>() {
877 unsafe {
878 let handled = core::ptr::read(&e as *const E as *const Self);
879 core::mem::forget(e);
880 handled
881 }
882 } else {
883 Self {
884 source: Error::new(e),
885 message: OnceLock::new(),
886 locations: LocationVec::new(),
887 contexts: None,
888 }
889 }
890 }
891
892 #[cfg(feature = "std")]
895 #[inline]
896 pub fn msg(message: impl Into<String>) -> Self {
897 let message = message.into();
898 let msg_lock = OnceLock::new();
899 let _ = msg_lock.set(message.clone());
900 Self {
901 source: Error::new(StringError(message)),
902 message: msg_lock,
903 locations: LocationVec::new(),
904 contexts: None,
905 chained: None,
906 }
907 }
908
909 #[cfg(not(feature = "std"))]
910 #[inline]
911 pub fn msg(message: impl Into<String>) -> Self {
912 let message = message.into();
913 let msg_lock = OnceLock::new();
914 let _ = msg_lock.set(message.clone());
915 Self {
916 source: Error::new(StringError(message)),
917 message: msg_lock,
918 locations: LocationVec::new(),
919 contexts: None,
920 }
921 }
922
923 #[doc(hidden)]
932 #[cfg(feature = "std")]
933 pub fn chain_after(mut self, previous: Self) -> Self {
934 let existing_chain = self.chained.take();
936
937 let new_previous = if let Some(existing) = existing_chain {
939 let mut prev = previous;
941 prev.chained = Some(existing);
942 prev
943 } else {
944 previous
945 };
946
947 self.chained = Some(Box::new(new_previous));
948 self
949 }
950
951 #[cfg(feature = "std")]
953 pub fn root(&self) -> &(dyn StdError + 'static) {
954 self.source.as_dyn_error()
955 }
956
957 #[cfg(feature = "std")]
959 #[inline]
960 pub fn downcast_ref<T: StdError + 'static>(&self) -> Option<&T> {
961 self.source.downcast_ref::<T>()
962 }
963
964 #[cfg(feature = "std")]
966 #[inline]
967 pub fn downcast<T: StdError + 'static>(self) -> core::result::Result<T, Self> {
968 if self.source.downcast_ref::<T>().is_some() {
969 let Self {
970 source,
971 locations,
972 contexts,
973 message,
974 chained,
975 } = self;
976 match source.downcast::<T>() {
977 Ok(e) => Ok(e),
978 Err(source) => Err(Self {
979 source,
980 locations,
981 contexts,
982 message,
983 chained,
984 }),
985 }
986 } else {
987 Err(self)
988 }
989 }
990
991 #[cfg(feature = "std")]
1009 pub fn chain_any<T: StdError + 'static>(&self) -> Option<&T> {
1010 if let Some(e) = self.source.downcast_ref::<T>() {
1012 return Some(e);
1013 }
1014
1015 let mut current: Option<&(dyn StdError + 'static)> = self.source.as_dyn_error().source();
1017 while let Some(err) = current {
1018 if let Some(e) = err.downcast_ref::<T>() {
1020 return Some(e);
1021 }
1022
1023 if let Some(handled) = err.downcast_ref::<Handled<Error>>() {
1025 if let Some(e) = handled.chain_any::<T>() {
1026 return Some(e);
1027 }
1028 }
1029
1030 current = err.source();
1031 }
1032
1033 if let Some(ref chained) = self.chained {
1035 if let Some(e) = chained.chain_any::<T>() {
1036 return Some(e);
1037 }
1038 }
1039
1040 None
1041 }
1042
1043 #[cfg(feature = "std")]
1062 pub fn chain_all<T: StdError + 'static>(&self) -> Vec<&T> {
1063 let mut matches = Vec::new();
1064
1065 if let Some(e) = self.source.downcast_ref::<T>() {
1067 matches.push(e);
1068 }
1069
1070 let mut current: Option<&(dyn StdError + 'static)> = self.source.as_dyn_error().source();
1072 while let Some(err) = current {
1073 if let Some(handled) = err.downcast_ref::<Handled<Error>>() {
1075 matches.extend(handled.chain_all::<T>());
1076 break; }
1078
1079 if let Some(e) = err.downcast_ref::<T>() {
1081 matches.push(e);
1082 }
1083
1084 current = err.source();
1085 }
1086
1087 if let Some(ref chained) = self.chained {
1089 matches.extend(chained.chain_all::<T>());
1090 }
1091
1092 matches
1093 }
1094}
1095
1096impl<E: fmt::Display> fmt::Display for Handled<E> {
1106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1107 let msg = self.message.get_or_init(|| self.source.to_string());
1108 writeln!(f, "{}", msg)?;
1109
1110 if !self.locations.is_empty() {
1111 writeln!(f, "\nTrace (most recent last):")?;
1112 for (idx, loc) in self.locations.iter().enumerate() {
1113 write!(f, " {}:{}:{}", loc.file, loc.line, loc.col)?;
1114
1115 if let Some(contexts) = &self.contexts {
1117 if let Some(ctx) = contexts.iter().find(|c| c.location_idx == idx as u16) {
1118 if let Some(msg) = &ctx.message {
1119 write!(f, "\n \u{2192} {}", msg)?;
1120 }
1121 for (k, v) in &ctx.attachments {
1122 write!(f, "\n {}: {}", k, v)?;
1123 }
1124 }
1125 }
1126 writeln!(f)?;
1127 }
1128 }
1129
1130 Ok(())
1131 }
1132}
1133
1134#[cfg(feature = "std")]
1140impl StdError for Handled<Error> {
1141 fn source(&self) -> Option<&(dyn StdError + 'static)> {
1142 Some(self.source.as_dyn_error())
1143 }
1144}
1145
1146#[doc(hidden)]
1153#[cfg(feature = "std")]
1154pub fn __wrap_any<E: StdError + Send + Sync + 'static>(e: E) -> Handled<Error> {
1155 use core::any::TypeId;
1156 if TypeId::of::<E>() == TypeId::of::<Handled<Error>>() {
1158 unsafe {
1160 let handled = core::ptr::read(&e as *const E as *const Handled<Error>);
1161 core::mem::forget(e);
1162 handled
1163 }
1164 } else {
1165 Handled::wrap(e)
1166 }
1167}
1168
1169impl From<&str> for Handled<Error> {
1174 fn from(s: &str) -> Self {
1175 Self::msg(s)
1176 }
1177}
1178
1179impl From<String> for Handled<Error> {
1180 fn from(s: String) -> Self {
1181 Self::msg(s)
1182 }
1183}
1184
1185macro_rules! impl_from_primitive {
1187 ($($t:ty),*) => {
1188 $(
1189 impl From<$t> for Handled<Error> {
1190 fn from(v: $t) -> Self {
1191 Self::msg(v.to_string())
1192 }
1193 }
1194 )*
1195 };
1196}
1197
1198impl_from_primitive!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
1199
1200impl From<()> for Handled<Error> {
1202 fn from(_: ()) -> Self {
1203 Self::msg("error")
1204 }
1205}
1206
1207#[cfg(feature = "std")]
1209impl From<Box<dyn core::fmt::Display + Send + Sync>> for Handled<Error> {
1210 fn from(e: Box<dyn core::fmt::Display + Send + Sync>) -> Self {
1211 Self::msg(e.to_string())
1212 }
1213}
1214
1215#[cfg(feature = "std")]
1216impl From<Box<dyn StdError + Send + Sync + 'static>> for Handled<Error> {
1217 fn from(e: Box<dyn StdError + Send + Sync + 'static>) -> Self {
1218 Self::wrap_box(e)
1219 }
1220}
1221
1222#[cfg(feature = "std")]
1227impl From<std::io::Error> for Handled<Error> {
1228 fn from(e: std::io::Error) -> Self {
1229 Self::wrap(e)
1230 }
1231}
1232
1233#[cfg(feature = "std")]
1234impl From<std::num::ParseIntError> for Handled<Error> {
1235 fn from(e: std::num::ParseIntError) -> Self {
1236 Self::wrap(e)
1237 }
1238}
1239
1240#[cfg(feature = "std")]
1241impl From<std::num::ParseFloatError> for Handled<Error> {
1242 fn from(e: std::num::ParseFloatError) -> Self {
1243 Self::wrap(e)
1244 }
1245}
1246
1247#[cfg(feature = "std")]
1248impl From<std::str::Utf8Error> for Handled<Error> {
1249 fn from(e: std::str::Utf8Error) -> Self {
1250 Self::wrap(e)
1251 }
1252}
1253
1254#[cfg(feature = "std")]
1255impl From<std::string::FromUtf8Error> for Handled<Error> {
1256 fn from(e: std::string::FromUtf8Error) -> Self {
1257 Self::wrap(e)
1258 }
1259}
1260
1261#[cfg(feature = "anyhow")]
1270impl From<anyhow::Error> for Handled<Error> {
1271 fn from(e: anyhow::Error) -> Self {
1272 match e.downcast::<Handled<Error>>() {
1273 Ok(h) => h,
1274 Err(e) => {
1275 let msg = e.to_string();
1276 let mut handled = Self::msg(msg);
1277 for (i, cause) in e.chain().skip(1).enumerate() {
1278 handled = handled
1279 .frame("<anyhow>", i as u32, 0)
1280 .ctx(cause.to_string());
1281 }
1282 handled
1283 }
1284 }
1285 }
1286}
1287
1288#[cfg(feature = "eyre")]
1293impl From<eyre::Report> for Handled<Error> {
1294 fn from(e: eyre::Report) -> Self {
1295 match e.downcast::<Handled<Error>>() {
1296 Ok(h) => h,
1297 Err(e) => {
1298 let msg = e.to_string();
1299 let mut handled = Self::msg(msg);
1300 for (i, cause) in e.chain().skip(1).enumerate() {
1301 handled = handled
1302 .frame("<eyre>", i as u32, 0)
1303 .ctx(cause.to_string());
1304 }
1305 handled
1306 }
1307 }
1308 }
1309}
1310
1311#[cfg(feature = "serde")]
1316mod serde_impl {
1317 use super::*;
1318 #[cfg(feature = "std")]
1319 use std::collections::BTreeMap;
1320 #[cfg(not(feature = "std"))]
1321 use alloc::collections::BTreeMap;
1322 use serde::{Deserialize, Deserializer, Serialize, Serializer};
1323
1324 impl Serialize for Value {
1326 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1327 match self {
1328 Value::String(s) => serializer.serialize_str(s),
1329 Value::Int(n) => serializer.serialize_i64(*n),
1330 Value::Uint(n) => serializer.serialize_u64(*n),
1331 Value::Float(n) => serializer.serialize_f64(*n),
1332 Value::Bool(b) => serializer.serialize_bool(*b),
1333 Value::Null => serializer.serialize_none(),
1334 }
1335 }
1336 }
1337
1338 impl<'de> Deserialize<'de> for Value {
1339 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1340 use serde::de::{self, Visitor};
1341
1342 struct ValueVisitor;
1343
1344 impl<'de> Visitor<'de> for ValueVisitor {
1345 type Value = Value;
1346
1347 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
1348 formatter.write_str("a string, number, boolean, or null")
1349 }
1350
1351 fn visit_bool<E: de::Error>(self, v: bool) -> Result<Value, E> {
1352 Ok(Value::Bool(v))
1353 }
1354
1355 fn visit_i64<E: de::Error>(self, v: i64) -> Result<Value, E> {
1356 Ok(Value::Int(v))
1357 }
1358
1359 fn visit_u64<E: de::Error>(self, v: u64) -> Result<Value, E> {
1360 Ok(Value::Uint(v))
1361 }
1362
1363 fn visit_f64<E: de::Error>(self, v: f64) -> Result<Value, E> {
1364 Ok(Value::Float(v))
1365 }
1366
1367 fn visit_str<E: de::Error>(self, v: &str) -> Result<Value, E> {
1368 Ok(Value::String(v.to_string()))
1369 }
1370
1371 fn visit_string<E: de::Error>(self, v: String) -> Result<Value, E> {
1372 Ok(Value::String(v))
1373 }
1374
1375 fn visit_none<E: de::Error>(self) -> Result<Value, E> {
1376 Ok(Value::Null)
1377 }
1378
1379 fn visit_unit<E: de::Error>(self) -> Result<Value, E> {
1380 Ok(Value::Null)
1381 }
1382 }
1383
1384 deserializer.deserialize_any(ValueVisitor)
1385 }
1386 }
1387
1388 #[derive(Serialize, Deserialize)]
1389 struct SerializedFrame {
1390 file: String,
1391 line: u32,
1392 col: u32,
1393 #[serde(skip_serializing_if = "Option::is_none")]
1394 message: Option<String>,
1395 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
1396 attachments: BTreeMap<String, Value>,
1397 }
1398
1399 #[derive(Serialize, Deserialize)]
1400 struct SerializedHandled {
1401 message: String,
1402 trace: Vec<SerializedFrame>,
1403 }
1404
1405 impl Serialize for Handled<Error> {
1407 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1408 let contexts = self.contexts.as_ref();
1409 let serialized = SerializedHandled {
1410 message: self.message().to_string(),
1411 trace: self
1412 .locations
1413 .iter()
1414 .enumerate()
1415 .map(|(idx, loc)| {
1416 let ctx = contexts.and_then(|c| c.iter().find(|e| e.location_idx == idx as u16));
1417 SerializedFrame {
1418 file: loc.file.to_string(),
1419 line: loc.line,
1420 col: loc.col,
1421 message: ctx.and_then(|c| c.message.clone()),
1422 attachments: ctx
1423 .map(|c| c.attachments.iter()
1424 .map(|(k, v)| (k.to_string(), v.clone()))
1425 .collect::<BTreeMap<_, _>>())
1426 .unwrap_or_default(),
1427 }
1428 })
1429 .collect(),
1430 };
1431 serialized.serialize(serializer)
1432 }
1433 }
1434
1435 impl<'de> Deserialize<'de> for Handled<Error> {
1436 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1437 let serialized = SerializedHandled::deserialize(deserializer)?;
1438
1439 let mut locations = LocationVec::new();
1440 let mut contexts = Vec::new();
1441
1442 for (idx, f) in serialized.trace.into_iter().enumerate() {
1443 let file: &'static str = Box::leak(f.file.into_boxed_str());
1446 locations.push(Location {
1447 file,
1448 line: f.line,
1449 col: f.col,
1450 });
1451
1452 if f.message.is_some() || !f.attachments.is_empty() {
1453 contexts.push(ContextEntry {
1454 location_idx: idx as u16,
1455 message: f.message,
1456 attachments: f.attachments
1457 .into_iter()
1458 .map(|(k, v)| (Cow::Owned(k), v))
1459 .collect(),
1460 });
1461 }
1462 }
1463
1464 Ok(Self {
1465 message: {
1466 let lock = OnceLock::new();
1467 let _ = lock.set(serialized.message.clone());
1468 lock
1469 },
1470 source: Error::new(StringError(serialized.message)),
1471 locations,
1472 contexts: if contexts.is_empty() { None } else { Some(contexts) },
1473 #[cfg(feature = "std")]
1474 chained: None,
1475 })
1476 }
1477 }
1478
1479 impl Serialize for Location {
1480 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1481 SerializedFrame {
1482 file: self.file.to_string(),
1483 line: self.line,
1484 col: self.col,
1485 message: None,
1486 attachments: BTreeMap::new(),
1487 }
1488 .serialize(serializer)
1489 }
1490 }
1491
1492 impl Serialize for FrameView<'_> {
1494 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1495 use serde::ser::SerializeStruct;
1496 let mut state = serializer.serialize_struct("FrameView", 5)?;
1497 state.serialize_field("file", self.file)?;
1498 state.serialize_field("line", &self.line)?;
1499 state.serialize_field("col", &self.col)?;
1500 if self.context.is_some() {
1501 state.serialize_field("message", &self.context)?;
1502 }
1503 if !self.attachments_inner.is_empty() {
1504 let map: BTreeMap<&str, &Value> = self.attachments_inner
1506 .iter()
1507 .map(|(k, v)| (k.as_ref(), v))
1508 .collect();
1509 state.serialize_field("attachments", &map)?;
1510 }
1511 state.end()
1512 }
1513 }
1514}