1use std::{fmt, mem};
2
3use crate::{Location, StaticStr};
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7pub(crate) struct Message {
8 type_name: StaticStr,
9 display: Box<str>,
10 index: usize,
11}
12
13impl Message {
14 pub(crate) const fn type_name(&self) -> &StaticStr {
15 &self.type_name
16 }
17
18 pub(crate) const fn display(&self) -> &str {
19 &self.display
20 }
21
22 pub(crate) const fn index(&self) -> usize {
23 self.index
24 }
25
26 pub(crate) const fn is_head(&self) -> bool {
27 self.index == usize::MAX
28 }
29}
30
31#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub struct Backtrace {
34 messages: Vec<Message>,
35 locations: Vec<Location>,
36}
37
38impl fmt::Debug for Backtrace {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 write!(f, "Backtrace {{ ... }}")
41 }
42}
43
44impl Backtrace {
45 #[doc(hidden)]
46 pub fn new() -> Self {
47 Self {
48 messages: Vec::new(),
49 locations: Vec::new(),
50 }
51 }
52
53 #[doc(hidden)]
54 pub fn with_head(type_name: &'static str, display: String) -> Self {
55 Self {
56 messages: vec![Message {
57 type_name: type_name.into(),
58 display: display.into(),
59 index: usize::MAX,
60 }],
61 locations: Vec::new(),
62 }
63 }
64
65 #[doc(hidden)]
66 pub fn push_error(&mut self, type_name: &'static str, display: String, location: Location) {
67 let index = self.locations.len();
68
69 self.messages.push(Message {
70 type_name: type_name.into(),
71 display: display.into(),
72 index,
73 });
74
75 self.attach_location(location);
76 }
77
78 pub(crate) const fn head_and_messages(&self) -> (Option<&Message>, &[Message]) {
79 let messages = self.messages.as_slice();
80
81 match messages {
82 [] => (None, &[]),
83 [first, rest @ ..] if first.is_head() => (Some(first), rest),
84 _ => (None, messages),
85 }
86 }
87
88 pub(crate) const fn locations(&self) -> &Vec<Location> {
89 &self.locations
90 }
91
92 const fn has_head(&self) -> bool {
93 matches!(self.messages.as_slice().first(), Some(msg) if msg.is_head())
94 }
95
96 #[inline]
97 pub(crate) fn attach_location(&mut self, location: Location) {
98 debug_assert!(
99 if self.has_head() {
100 self.messages.len() > 1
101 } else {
102 !self.messages.is_empty()
103 }
104 );
105
106 self.locations.push(location);
107 }
108
109 pub(crate) fn take(&mut self) -> Backtrace {
110 Backtrace {
111 messages: mem::take(&mut self.messages),
112 locations: mem::take(&mut self.locations),
113 }
114 }
115}