dangerous/display/
error.rs1use crate::error::{self, Context};
2use crate::fmt::{self, Write};
3use crate::input::{Bytes, Input, Private};
4
5use super::{DisplayBase, InputDisplay, PreferredFormat};
6
7const DEFAULT_MAX_WIDTH: usize = 80;
8const INVALID_SPAN_ERROR: &str = "\
9note: error span is not within the error input indicating the
10 concrete error being used has a bug. Consider raising an
11 issue with the maintainer!
12";
13
14#[derive(Clone)]
16#[must_use = "error displays must be written"]
17pub struct ErrorDisplay<'a, T> {
18 error: &'a T,
19 banner: bool,
20 format: PreferredFormat,
21 input_max_width: usize,
22}
23
24impl<'a, 'i, T> ErrorDisplay<'a, T>
25where
26 T: error::Details<'i>,
27{
28 pub fn new(error: &'a T) -> Self {
30 let format = if error.input().is_string() {
31 PreferredFormat::Str
32 } else {
33 PreferredFormat::Bytes
34 };
35 Self {
36 error,
37 format,
38 banner: false,
39 input_max_width: DEFAULT_MAX_WIDTH,
40 }
41 }
42
43 pub fn from_formatter(error: &'a T, f: &fmt::Formatter<'_>) -> Self {
45 if f.alternate() {
46 Self::new(error).str_hint()
47 } else {
48 Self::new(error)
49 }
50 }
51
52 pub fn banner(mut self, value: bool) -> Self {
54 self.banner = value;
55 self
56 }
57
58 pub fn input_max_width(mut self, value: usize) -> Self {
60 self.input_max_width = value;
61 self
62 }
63
64 pub fn str_hint(self) -> Self {
66 match self.format {
67 PreferredFormat::Bytes | PreferredFormat::BytesAscii => {
68 self.format(PreferredFormat::Str)
69 }
70 _ => self,
71 }
72 }
73
74 pub fn format(mut self, format: PreferredFormat) -> Self {
76 self.format = format;
77 self
78 }
79
80 fn write_sections(&self, w: &mut dyn Write) -> fmt::Result {
81 let input = self.error.input();
82 let root = self.error.backtrace().root();
83 w.write_str("error attempting to ")?;
85 root.operation().description(w)?;
86 w.write_str(": ")?;
87 self.error.description(w)?;
88 w.write_char('\n')?;
89 let input_display = self.configure_input_display(input.display());
91 let input = input.into_bytes();
92 if let Some(expected_value) = self.error.expected() {
93 let expected_display = self.configure_input_display(expected_value.display());
94 w.write_str("expected:\n")?;
95 write_input(w, expected_display, false)?;
96 w.write_str("in:\n")?;
97 }
98 if root.span.is_within(input.span()) {
99 write_input(w, input_display.span(root.span, self.input_max_width), true)?;
100 } else {
101 w.write_str(INVALID_SPAN_ERROR)?;
102 w.write_str("input:\n")?;
103 write_input(w, input_display, false)?;
104 }
105 w.write_str("additional:\n ")?;
107 if let Some(span_range) = root.span.range_of(input.span()) {
108 match self.format {
109 PreferredFormat::Str | PreferredFormat::StrCjk | PreferredFormat::BytesAscii => {
110 w.write_str("error line: ")?;
111 w.write_usize(line_offset(&input, span_range.start))?;
112 w.write_str(", ")?;
113 }
114 _ => (),
115 }
116 w.write_str("error offset: ")?;
117 w.write_usize(span_range.start)?;
118 w.write_str(", input length: ")?;
119 w.write_usize(input.len())?;
120 } else {
121 w.write_str("error: ")?;
122 DisplayBase::fmt(&root.span, w)?;
123 w.write_str("input: ")?;
124 DisplayBase::fmt(&input.span(), w)?;
125 }
126 w.write_char('\n')?;
127 w.write_str("backtrace:")?;
129 let mut child_index = 1;
130 let mut last_parent_depth = 0;
131 let write_success = self.error.backtrace().walk(&mut |parent_depth, context| {
132 let mut write = || {
133 w.write_str("\n ")?;
134 if parent_depth == last_parent_depth {
135 w.write_str(" ")?;
136 w.write_usize(child_index)?;
137 child_index += 1;
138 } else {
139 child_index = 1;
140 last_parent_depth = parent_depth;
141 w.write_usize(parent_depth)?;
142 }
143 w.write_str(". `")?;
144 context.operation().description(w)?;
145 w.write_char('`')?;
146 if context.has_expected() {
147 w.write_str(" (expected ")?;
148 context.expected(w)?;
149 w.write_char(')')?;
150 }
151 fmt::Result::Ok(())
152 };
153 write().is_ok()
154 });
155 if write_success {
156 Ok(())
157 } else {
158 Err(fmt::Error)
159 }
160 }
161
162 fn configure_input_display<'b>(&self, display: InputDisplay<'b>) -> InputDisplay<'b> {
163 display.format(self.format)
164 }
165}
166
167impl<'a, 'i, T> fmt::DisplayBase for ErrorDisplay<'a, T>
168where
169 T: error::Details<'i>,
170{
171 fn fmt(&self, w: &mut dyn Write) -> fmt::Result {
172 if self.banner {
173 w.write_str("\n-- INPUT ERROR ---------------------------------------------\n")?;
174 self.write_sections(w)?;
175 w.write_str("\n------------------------------------------------------------\n")
176 } else {
177 self.write_sections(w)
178 }
179 }
180}
181
182impl<'a, 'i, T> fmt::Debug for ErrorDisplay<'a, T>
183where
184 T: error::Details<'i>,
185{
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 fmt::DisplayBase::fmt(self, f)
188 }
189}
190
191impl<'a, 'i, T> fmt::Display for ErrorDisplay<'a, T>
192where
193 T: error::Details<'i>,
194{
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 fmt::DisplayBase::fmt(self, f)
197 }
198}
199
200fn line_offset(input: &Bytes<'_>, span_offset: usize) -> usize {
201 match input.clone().split_at_opt(span_offset) {
202 Some((before_span, _)) => before_span.count(b'\n') + 1,
203 None => 0,
206 }
207}
208
209fn write_input(w: &mut dyn Write, input: InputDisplay<'_>, underline: bool) -> fmt::Result {
210 let input = input.prepare();
211 w.write_str("> ")?;
212 fmt::DisplayBase::fmt(&input, w)?;
213 w.write_char('\n')?;
214 if underline {
215 w.write_str(" ")?;
216 fmt::DisplayBase::fmt(&input.underline(), w)?;
217 w.write_char('\n')?;
218 }
219 Ok(())
220}