musli_common/context/
rich_error.rs

1use core::fmt;
2use core::mem::take;
3use core::ops::Range;
4
5/// A collected error which has been context decorated.
6pub struct RichError<'a, S, E> {
7    path: &'a [Step<S>],
8    path_cap: usize,
9    range: Range<usize>,
10    error: &'a E,
11}
12
13impl<'a, S, E> RichError<'a, S, E> {
14    pub(crate) fn new(
15        path: &'a [Step<S>],
16        path_cap: usize,
17        range: Range<usize>,
18        error: &'a E,
19    ) -> Self {
20        Self {
21            path,
22            path_cap,
23            range,
24            error,
25        }
26    }
27}
28
29impl<'a, S, E> fmt::Display for RichError<'a, S, E>
30where
31    S: fmt::Display,
32    E: fmt::Display,
33{
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        let path = format_path(self.path, self.path_cap);
36
37        if self.range.start != 0 || self.range.end != 0 {
38            if self.range.start == self.range.end {
39                write!(f, "{path}: {} (at byte {})", self.error, self.range.start)?;
40            } else {
41                write!(
42                    f,
43                    "{path}: {} (at bytes {}-{})",
44                    self.error, self.range.start, self.range.end
45                )?;
46            }
47        } else {
48            write!(f, "{path}: {}", self.error)?;
49        }
50
51        Ok(())
52    }
53}
54
55/// A single traced step.
56#[derive(Debug, Clone)]
57pub(crate) enum Step<S> {
58    Struct(&'static str),
59    Enum(&'static str),
60    Variant(&'static str),
61    Named(&'static str),
62    Unnamed(u32),
63    Index(usize),
64    Key(S),
65}
66
67fn format_path<S>(path: &[Step<S>], path_cap: usize) -> impl fmt::Display + '_
68where
69    S: fmt::Display,
70{
71    FormatPath { path, path_cap }
72}
73
74struct FormatPath<'a, S> {
75    path: &'a [Step<S>],
76    path_cap: usize,
77}
78
79impl<'a, S> fmt::Display for FormatPath<'a, S>
80where
81    S: fmt::Display,
82{
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        let mut has_type = false;
85        let mut has_field = false;
86        let mut level = 0;
87
88        for step in self.path {
89            match step {
90                Step::Struct(name) => {
91                    if take(&mut has_field) {
92                        write!(f, " = ")?;
93                    }
94
95                    write!(f, "{name}")?;
96                    has_type = true;
97                }
98                Step::Enum(name) => {
99                    if take(&mut has_field) {
100                        write!(f, " = ")?;
101                    }
102
103                    write!(f, "{name}::")?;
104                }
105                Step::Variant(name) => {
106                    if take(&mut has_field) {
107                        write!(f, " = ")?;
108                    }
109
110                    write!(f, "{name}")?;
111                    has_type = true;
112                }
113                Step::Named(name) => {
114                    if take(&mut has_type) {
115                        write!(f, " {{ ")?;
116                        level += 1;
117                    }
118
119                    write!(f, ".{name}")?;
120                    has_field = true;
121                }
122                Step::Unnamed(index) => {
123                    if take(&mut has_type) {
124                        write!(f, " {{ ")?;
125                        level += 1;
126                    }
127
128                    write!(f, ".{index}")?;
129                    has_field = true;
130                }
131                Step::Index(index) => {
132                    if take(&mut has_type) {
133                        write!(f, " {{ ")?;
134                        level += 1;
135                    }
136
137                    write!(f, "[{index}]")?;
138                    has_field = true;
139                }
140                Step::Key(key) => {
141                    if take(&mut has_type) {
142                        write!(f, " {{ ")?;
143                        level += 1;
144                    }
145
146                    write!(f, "[{}]", key)?;
147                    has_field = true;
148                }
149            }
150        }
151
152        for _ in 0..level {
153            write!(f, " }}")?;
154        }
155
156        match self.path_cap {
157            0 => {}
158            1 => write!(f, " .. *one capped step*")?,
159            n => write!(f, " .. *{n} capped steps*")?,
160        }
161
162        Ok(())
163    }
164}