doclog/blocks/
stack.rs

1use crate::blocks::{StackTraceBlock, TextBlock};
2use crate::constants::{
3    BOTTOM_RIGHT_CORNER, HORIZONTAL_BAR, RIGHT_ARROW, TOP_RIGHT_CORNER, VERTICAL_BAR,
4    VERTICAL_RIGHT_BAR,
5};
6use crate::printer::{Printable, Printer, PrinterFormat};
7use crate::utils::whitespaces::build_space_string;
8use crate::LogLevel;
9use const_format::concatcp;
10use std::borrow::Cow;
11use std::fmt::Display;
12use std::mem;
13use yansi::Style;
14
15/// An error stack block.
16#[derive(Default, Debug, Clone, Eq, PartialEq)]
17pub struct StackBlock<'a> {
18    pub message: TextBlock<'a>,
19    pub traces: Vec<StackTraceBlock<'a>>,
20    pub cause: Option<Box<StackBlock<'a>>>,
21    pub show_stack_numbers: bool,
22
23    /// Whether to print the stack in the wrapped-by format.
24    pub wrapped_by_format: bool,
25}
26
27impl<'a> StackBlock<'a> {
28    // CONSTRUCTORS -----------------------------------------------------------
29
30    /// Creates a new empty [StackBlock].
31    #[inline(always)]
32    pub fn new() -> Self {
33        StackBlock::default()
34    }
35
36    // BUILDERS ---------------------------------------------------------------
37
38    /// Sets the message.
39    #[inline(always)]
40    pub fn message(mut self, message: impl Into<TextBlock<'a>>) -> Self {
41        self.message = message.into();
42        self
43    }
44
45    /// Add a stack trace.
46    #[inline(always)]
47    pub fn add_stack_trace(mut self, stack_trace: StackTraceBlock<'a>) -> Self {
48        self.traces.push(stack_trace);
49        self
50    }
51
52    /// Sets the cause.
53    #[inline(always)]
54    pub fn cause(mut self, cause: StackBlock<'a>) -> Self {
55        self.cause = Some(Box::new(cause));
56        self
57    }
58
59    /// Sets whether to show stack numbers.
60    #[inline(always)]
61    pub fn show_stack_numbers(mut self, show_stack_numbers: bool) -> Self {
62        self.show_stack_numbers = show_stack_numbers;
63        self
64    }
65
66    /// Sets whether to print the stack in the wrapped-by format.
67    #[inline(always)]
68    pub fn wrapped_by_format(mut self, wrapped_by_format: bool) -> Self {
69        self.wrapped_by_format = wrapped_by_format;
70        self
71    }
72
73    // METHODS ----------------------------------------------------------------
74
75    /// Count traces of the stack and its cause recursively.
76    fn count_traces(&self) -> usize {
77        self.traces.len() + self.cause.as_ref().map_or(0, |v| v.count_traces())
78    }
79
80    /// Prints the stack block with the given options following the caused by format, i.e.
81    /// the top error is printed first and then what caused it.
82    fn print_as_caused_by(
83        &self,
84        printer: &mut Printer<'a>,
85        initial_trace_number: usize,
86        max_trace_digits: usize,
87        is_cause: bool,
88    ) {
89        // Message
90        if is_cause {
91            printer.push_styled_text(
92                concatcp!(
93                    '\n',
94                    VERTICAL_RIGHT_BAR,
95                    HORIZONTAL_BAR,
96                    HORIZONTAL_BAR,
97                    HORIZONTAL_BAR,
98                    RIGHT_ARROW,
99                    " Caused by: "
100                ),
101                Style::new().bold().fg(printer.level.color()),
102            );
103        } else if self.message.is_empty() {
104            printer.push_styled_text(
105                concatcp!(BOTTOM_RIGHT_CORNER, HORIZONTAL_BAR, ' '),
106                Style::new().bold().fg(printer.level.color()),
107            );
108        } else {
109            printer.push_styled_text(
110                concatcp!(BOTTOM_RIGHT_CORNER, HORIZONTAL_BAR, RIGHT_ARROW, ' '),
111                Style::new().bold().fg(printer.level.color()),
112            );
113        }
114
115        {
116            let mut message_printer = printer.derive();
117            self.message.print(&mut message_printer);
118
119            let prefix = TextBlock::new().add_styled_text(
120                if is_cause {
121                    concatcp!(VERTICAL_BAR, "     ")
122                } else {
123                    concatcp!(VERTICAL_BAR, "   ")
124                },
125                Style::new().bold().fg(printer.level.color()),
126            );
127
128            message_printer.indent(&prefix.sections, false);
129            printer.append(message_printer);
130        }
131
132        // Traces
133        let trace_prefix = TextBlock::new().add_styled_text(
134            concatcp!(VERTICAL_BAR, "  "),
135            Style::new().bold().fg(printer.level.color()),
136        );
137        let full_trace_prefix = trace_prefix.clone().add_styled_text(
138            build_space_string(max_trace_digits + 2),
139            Style::new().bold().fg(printer.level.color()),
140        );
141
142        let mut trace_printer = printer.derive();
143        let mut next_trace_number = 0;
144        for trace in self.traces.iter() {
145            printer.push_plain_text(Cow::Borrowed("\n"));
146            trace_prefix.print(printer);
147
148            let number = self.traces.len() - next_trace_number + initial_trace_number;
149            next_trace_number += 1;
150
151            if self.show_stack_numbers {
152                printer.push_styled_text(
153                    format!("[{:>width$}] ", number, width = max_trace_digits),
154                    Style::new().bold().fg(printer.level.color()),
155                );
156            } else {
157                printer.push_styled_text(" at ", Style::new().bold().fg(printer.level.color()));
158            }
159
160            trace.print(&mut trace_printer);
161            trace_printer.indent(&full_trace_prefix.sections, false);
162            printer.append(mem::replace(&mut trace_printer, printer.derive()));
163        }
164
165        // Cause
166        if let Some(cause) = &self.cause {
167            cause.print_as_caused_by(
168                printer,
169                next_trace_number + initial_trace_number,
170                max_trace_digits,
171                true,
172            );
173        }
174
175        // Final line
176        if !is_cause {
177            printer.push_styled_text(
178                concatcp!('\n', TOP_RIGHT_CORNER, HORIZONTAL_BAR),
179                Style::new().bold().fg(printer.level.color()),
180            );
181        }
182    }
183
184    /// Prints the stack block with the given options following the wrapped by format, i.e.
185    /// the innermost error is printed first and then what wrapped it.
186    fn print_as_wrapped_by(
187        &self,
188        printer: &mut Printer<'a>,
189        initial_trace_number: usize,
190        max_trace_digits: usize,
191        is_root: bool,
192    ) {
193        let is_cause = match &self.cause {
194            Some(cause) => {
195                cause.print_as_wrapped_by(
196                    printer,
197                    initial_trace_number + self.traces.len(),
198                    max_trace_digits,
199                    false,
200                );
201                true
202            }
203            None => false,
204        };
205
206        // Message.
207        if is_cause {
208            printer.push_styled_text(
209                concatcp!(
210                    '\n',
211                    VERTICAL_RIGHT_BAR,
212                    HORIZONTAL_BAR,
213                    HORIZONTAL_BAR,
214                    HORIZONTAL_BAR,
215                    RIGHT_ARROW,
216                    " Wrapped by: "
217                ),
218                Style::new().bold().fg(printer.level.color()),
219            );
220        } else if self.message.is_empty() {
221            printer.push_styled_text(
222                concatcp!(BOTTOM_RIGHT_CORNER, HORIZONTAL_BAR, ' '),
223                Style::new().bold().fg(printer.level.color()),
224            );
225        } else {
226            printer.push_styled_text(
227                concatcp!(BOTTOM_RIGHT_CORNER, HORIZONTAL_BAR, RIGHT_ARROW, ' '),
228                Style::new().bold().fg(printer.level.color()),
229            );
230        }
231
232        {
233            let mut message_printer = printer.derive();
234            self.message.print(&mut message_printer);
235
236            let prefix = TextBlock::new().add_styled_text(
237                if is_cause {
238                    concatcp!(VERTICAL_BAR, "     ")
239                } else {
240                    concatcp!(VERTICAL_BAR, "   ")
241                },
242                Style::new().bold().fg(printer.level.color()),
243            );
244
245            message_printer.indent(&prefix.sections, false);
246            printer.append(message_printer);
247        }
248
249        // Traces
250        let trace_prefix = TextBlock::new().add_styled_text(
251            concatcp!(VERTICAL_BAR, "  "),
252            Style::new().bold().fg(printer.level.color()),
253        );
254        let full_trace_prefix = trace_prefix.clone().add_styled_text(
255            build_space_string(max_trace_digits + 2),
256            Style::new().bold().fg(printer.level.color()),
257        );
258
259        let mut trace_printer = printer.derive();
260        for (next_trace_number, trace) in self.traces.iter().enumerate() {
261            printer.push_plain_text(Cow::Borrowed("\n"));
262            trace_prefix.print(printer);
263
264            if self.show_stack_numbers {
265                let number = self.traces.len() - next_trace_number + initial_trace_number;
266                printer.push_styled_text(
267                    format!("[{:>width$}] ", number, width = max_trace_digits),
268                    Style::new().bold().fg(printer.level.color()),
269                );
270            } else {
271                printer.push_styled_text(" at ", Style::new().bold().fg(printer.level.color()));
272            }
273
274            trace.print(&mut trace_printer);
275            trace_printer.indent(&full_trace_prefix.sections, false);
276            printer.append(mem::replace(&mut trace_printer, printer.derive()));
277        }
278
279        // Final line
280        if is_root {
281            printer.push_styled_text(
282                concatcp!('\n', TOP_RIGHT_CORNER, HORIZONTAL_BAR),
283                Style::new().bold().fg(printer.level.color()),
284            );
285        }
286    }
287
288    /// Makes this type owned, i.e. changing the lifetime to `'static`.
289    pub fn make_owned(self) -> StackBlock<'static> {
290        StackBlock {
291            message: self.message.make_owned(),
292            traces: self.traces.into_iter().map(|v| v.make_owned()).collect(),
293            cause: self.cause.map(|v| Box::new(v.make_owned())),
294            show_stack_numbers: self.show_stack_numbers,
295            wrapped_by_format: self.wrapped_by_format,
296        }
297    }
298}
299
300impl<'a> Printable<'a> for StackBlock<'a> {
301    fn print<'s>(&'s self, printer: &mut Printer<'a>)
302    where
303        'a: 's,
304    {
305        let max_trace_digits = format!("{}", self.count_traces()).len();
306
307        if self.wrapped_by_format {
308            self.print_as_wrapped_by(printer, 0, max_trace_digits, true)
309        } else {
310            self.print_as_caused_by(printer, 0, max_trace_digits, false)
311        }
312    }
313}
314
315impl<'a> Display for StackBlock<'a> {
316    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
317        let mut printer = Printer::new(LogLevel::trace(), PrinterFormat::Plain);
318        self.print(&mut printer);
319        printer.fmt(f, PrinterFormat::Plain)
320    }
321}
322
323// ----------------------------------------------------------------------------
324// ----------------------------------------------------------------------------
325// ----------------------------------------------------------------------------
326
327#[cfg(test)]
328mod tests {
329    use super::*;
330
331    #[test]
332    fn test_plain() {
333        // Empty
334        let log = StackBlock::new();
335        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
336
337        assert_eq!(text, "╭─ \n╰─");
338
339        // Message
340        let log = StackBlock::new().message(TextBlock::new_plain("This is\na message"));
341        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
342
343        assert_eq!(text, "╭─▶ This is\n│   a message\n╰─");
344
345        // Traces without numbers
346        let log = StackBlock::new()
347            .add_stack_trace(
348                StackTraceBlock::new()
349                    .message(TextBlock::new_plain("This is a \n message"))
350                    .file_location(TextBlock::new_plain("/a/b/c"))
351                    .code_path(TextBlock::new_plain("crate::x")),
352            )
353            .add_stack_trace(
354                StackTraceBlock::new()
355                    .message(TextBlock::new_plain("This is a \n message2"))
356                    .file_location(TextBlock::new_plain("/a/b/c/2"))
357                    .code_path(TextBlock::new_plain("crate::x::2")),
358            );
359        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
360
361        assert_eq!(text, "╭─ \n│   at /a/b/c(crate::x) - This is a \n│      message\n│   at /a/b/c/2(crate::x::2) - This is a \n│      message2\n╰─");
362
363        // Traces with numbers
364        let log = StackBlock::new()
365            .add_stack_trace(
366                StackTraceBlock::new()
367                    .message(TextBlock::new_plain("This is a \n message"))
368                    .file_location(TextBlock::new_plain("/a/b/c"))
369                    .code_path(TextBlock::new_plain("crate::x")),
370            )
371            .add_stack_trace(
372                StackTraceBlock::new()
373                    .message(TextBlock::new_plain("This is a \n message2"))
374                    .file_location(TextBlock::new_plain("/a/b/c/2"))
375                    .code_path(TextBlock::new_plain("crate::x::2")),
376            )
377            .show_stack_numbers(true);
378        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
379
380        assert_eq!(text, "╭─ \n│  [2] /a/b/c(crate::x) - This is a \n│      message\n│  [1] /a/b/c/2(crate::x::2) - This is a \n│      message2\n╰─");
381
382        // All
383        let log = StackBlock::new()
384            .message(TextBlock::new_plain("This is\na message"))
385            .add_stack_trace(
386                StackTraceBlock::new()
387                    .message(TextBlock::new_plain("This is a \n message"))
388                    .file_location(TextBlock::new_plain("/a/b/c"))
389                    .code_path(TextBlock::new_plain("crate::x")),
390            )
391            .add_stack_trace(
392                StackTraceBlock::new()
393                    .message(TextBlock::new_plain("This is a \n message2"))
394                    .file_location(TextBlock::new_plain("/a/b/c/2"))
395                    .code_path(TextBlock::new_plain("crate::x::2")),
396            )
397            .show_stack_numbers(true);
398        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
399
400        assert_eq!(text, "╭─▶ This is\n│   a message\n│  [2] /a/b/c(crate::x) - This is a \n│      message\n│  [1] /a/b/c/2(crate::x::2) - This is a \n│      message2\n╰─");
401    }
402
403    #[test]
404    fn test_styled() {
405        // Empty
406        let log = StackBlock::new();
407        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
408
409        println!("{}", text);
410        assert_eq!(text, "\u{1b}[1;31m╭─ \n╰─\u{1b}[0m");
411
412        // Message
413        let log = StackBlock::new().message(TextBlock::new_plain("This is\na message"));
414        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
415
416        println!("{}", text);
417        assert_eq!(text, "\u{1b}[1;31m╭─▶ \u{1b}[0mThis is\n\u{1b}[1;31m│   \u{1b}[0ma message\n\u{1b}[1;31m╰─\u{1b}[0m");
418
419        // Traces without numbers
420        let log = StackBlock::new()
421            .add_stack_trace(
422                StackTraceBlock::new()
423                    .message(TextBlock::new_plain("This is a \n message"))
424                    .file_location(TextBlock::new_plain("/a/b/c"))
425                    .code_path(TextBlock::new_plain("crate::x")),
426            )
427            .add_stack_trace(
428                StackTraceBlock::new()
429                    .message(TextBlock::new_plain("This is a \n message2"))
430                    .file_location(TextBlock::new_plain("/a/b/c/2"))
431                    .code_path(TextBlock::new_plain("crate::x::2")),
432            );
433        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
434
435        println!("{}", text);
436        assert_eq!(text, "\u{1b}[1;31m╭─ \n│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
437
438        // Traces with numbers
439        let log = StackBlock::new()
440            .add_stack_trace(
441                StackTraceBlock::new()
442                    .message(TextBlock::new_plain("This is a \n message"))
443                    .file_location(TextBlock::new_plain("/a/b/c"))
444                    .code_path(TextBlock::new_plain("crate::x")),
445            )
446            .add_stack_trace(
447                StackTraceBlock::new()
448                    .message(TextBlock::new_plain("This is a \n message2"))
449                    .file_location(TextBlock::new_plain("/a/b/c/2"))
450                    .code_path(TextBlock::new_plain("crate::x::2")),
451            )
452            .show_stack_numbers(true);
453        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
454
455        println!("{}", text);
456        assert_eq!(text, "\u{1b}[1;31m╭─ \n│  [2] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [1] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
457
458        // All
459        let log = StackBlock::new()
460            .message(TextBlock::new_plain("This is\na message"))
461            .add_stack_trace(
462                StackTraceBlock::new()
463                    .message(TextBlock::new_plain("This is a \n message"))
464                    .file_location(TextBlock::new_plain("/a/b/c"))
465                    .code_path(TextBlock::new_plain("crate::x")),
466            )
467            .add_stack_trace(
468                StackTraceBlock::new()
469                    .message(TextBlock::new_plain("This is a \n message2"))
470                    .file_location(TextBlock::new_plain("/a/b/c/2"))
471                    .code_path(TextBlock::new_plain("crate::x::2")),
472            )
473            .show_stack_numbers(true);
474        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
475
476        println!("{}", text);
477        assert_eq!(text, "\u{1b}[1;31m╭─▶ \u{1b}[0mThis is\n\u{1b}[1;31m│   \u{1b}[0ma message\n\u{1b}[1;31m│  [2] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [1] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
478    }
479
480    #[test]
481    fn test_plain_as_caused_by_format() {
482        let cause = StackBlock::new()
483            .message(TextBlock::new_plain("Cause\nnumber2"))
484            .add_stack_trace(
485                StackTraceBlock::new()
486                    .message(TextBlock::new_plain("This is a \n message"))
487                    .file_location(TextBlock::new_plain("/a/b/c"))
488                    .code_path(TextBlock::new_plain("crate::x")),
489            )
490            .add_stack_trace(
491                StackTraceBlock::new()
492                    .message(TextBlock::new_plain("This is a \n message2"))
493                    .file_location(TextBlock::new_plain("/a/b/c/2"))
494                    .code_path(TextBlock::new_plain("crate::x::2")),
495            )
496            .show_stack_numbers(true);
497        let cause = cause
498            .clone()
499            .message("Cause\nnumber1")
500            .show_stack_numbers(false)
501            .cause(cause.clone());
502
503        // Empty
504        let log = StackBlock::new().cause(cause.clone());
505        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
506
507        assert_eq!(text, "╭─ \n├───▶ Caused by: Cause\n│     number1\n│   at /a/b/c(crate::x) - This is a \n│      message\n│   at /a/b/c/2(crate::x::2) - This is a \n│      message2\n├───▶ Caused by: Cause\n│     number2\n│  [4] /a/b/c(crate::x) - This is a \n│      message\n│  [3] /a/b/c/2(crate::x::2) - This is a \n│      message2\n╰─");
508
509        // Message
510        let log = StackBlock::new()
511            .message(TextBlock::new_plain("This is\na message"))
512            .cause(cause.clone());
513        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
514
515        assert_eq!(text, "╭─▶ This is\n│   a message\n├───▶ Caused by: Cause\n│     number1\n│   at /a/b/c(crate::x) - This is a \n│      message\n│   at /a/b/c/2(crate::x::2) - This is a \n│      message2\n├───▶ Caused by: Cause\n│     number2\n│  [4] /a/b/c(crate::x) - This is a \n│      message\n│  [3] /a/b/c/2(crate::x::2) - This is a \n│      message2\n╰─");
516
517        // Traces without numbers
518        let log = StackBlock::new()
519            .add_stack_trace(
520                StackTraceBlock::new()
521                    .message(TextBlock::new_plain("This is a \n message"))
522                    .file_location(TextBlock::new_plain("/a/b/c"))
523                    .code_path(TextBlock::new_plain("crate::x")),
524            )
525            .add_stack_trace(
526                StackTraceBlock::new()
527                    .message(TextBlock::new_plain("This is a \n message2"))
528                    .file_location(TextBlock::new_plain("/a/b/c/2"))
529                    .code_path(TextBlock::new_plain("crate::x::2")),
530            )
531            .cause(cause.clone());
532        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
533
534        assert_eq!(text, "╭─ \n│   at /a/b/c(crate::x) - This is a \n│      message\n│   at /a/b/c/2(crate::x::2) - This is a \n│      message2\n├───▶ Caused by: Cause\n│     number1\n│   at /a/b/c(crate::x) - This is a \n│      message\n│   at /a/b/c/2(crate::x::2) - This is a \n│      message2\n├───▶ Caused by: Cause\n│     number2\n│  [6] /a/b/c(crate::x) - This is a \n│      message\n│  [5] /a/b/c/2(crate::x::2) - This is a \n│      message2\n╰─");
535
536        // Traces with numbers
537        let log = StackBlock::new()
538            .add_stack_trace(
539                StackTraceBlock::new()
540                    .message(TextBlock::new_plain("This is a \n message"))
541                    .file_location(TextBlock::new_plain("/a/b/c"))
542                    .code_path(TextBlock::new_plain("crate::x")),
543            )
544            .add_stack_trace(
545                StackTraceBlock::new()
546                    .message(TextBlock::new_plain("This is a \n message2"))
547                    .file_location(TextBlock::new_plain("/a/b/c/2"))
548                    .code_path(TextBlock::new_plain("crate::x::2")),
549            )
550            .show_stack_numbers(true)
551            .cause(cause.clone());
552        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
553
554        assert_eq!(text, "╭─ \n│  [2] /a/b/c(crate::x) - This is a \n│      message\n│  [1] /a/b/c/2(crate::x::2) - This is a \n│      message2\n├───▶ Caused by: Cause\n│     number1\n│   at /a/b/c(crate::x) - This is a \n│      message\n│   at /a/b/c/2(crate::x::2) - This is a \n│      message2\n├───▶ Caused by: Cause\n│     number2\n│  [6] /a/b/c(crate::x) - This is a \n│      message\n│  [5] /a/b/c/2(crate::x::2) - This is a \n│      message2\n╰─");
555
556        // All
557        let log = StackBlock::new()
558            .message(TextBlock::new_plain("This is\na message"))
559            .add_stack_trace(
560                StackTraceBlock::new()
561                    .message(TextBlock::new_plain("This is a \n message"))
562                    .file_location(TextBlock::new_plain("/a/b/c"))
563                    .code_path(TextBlock::new_plain("crate::x")),
564            )
565            .add_stack_trace(
566                StackTraceBlock::new()
567                    .message(TextBlock::new_plain("This is a \n message2"))
568                    .file_location(TextBlock::new_plain("/a/b/c/2"))
569                    .code_path(TextBlock::new_plain("crate::x::2")),
570            )
571            .show_stack_numbers(true)
572            .cause(cause.clone());
573        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Plain);
574
575        assert_eq!(text, "╭─▶ This is\n│   a message\n│  [2] /a/b/c(crate::x) - This is a \n│      message\n│  [1] /a/b/c/2(crate::x::2) - This is a \n│      message2\n├───▶ Caused by: Cause\n│     number1\n│   at /a/b/c(crate::x) - This is a \n│      message\n│   at /a/b/c/2(crate::x::2) - This is a \n│      message2\n├───▶ Caused by: Cause\n│     number2\n│  [6] /a/b/c(crate::x) - This is a \n│      message\n│  [5] /a/b/c/2(crate::x::2) - This is a \n│      message2\n╰─");
576    }
577
578    #[test]
579    fn test_styled_as_caused_by_format() {
580        let cause = StackBlock::new()
581            .message(TextBlock::new_plain("Cause\nnumber2"))
582            .add_stack_trace(
583                StackTraceBlock::new()
584                    .message(TextBlock::new_plain("This is a \n message"))
585                    .file_location(TextBlock::new_plain("/a/b/c"))
586                    .code_path(TextBlock::new_plain("crate::x")),
587            )
588            .add_stack_trace(
589                StackTraceBlock::new()
590                    .message(TextBlock::new_plain("This is a \n message2"))
591                    .file_location(TextBlock::new_plain("/a/b/c/2"))
592                    .code_path(TextBlock::new_plain("crate::x::2")),
593            )
594            .show_stack_numbers(true);
595        let cause = cause
596            .clone()
597            .message("Cause\nnumber1")
598            .show_stack_numbers(false)
599            .cause(cause.clone());
600
601        // Empty
602        let log = StackBlock::new().cause(cause.clone());
603        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
604
605        println!("{}", text);
606        assert_eq!(text, "\u{1b}[1;31m╭─ \n├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber2\n\u{1b}[1;31m│  [4] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [3] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
607
608        // Message
609        let log = StackBlock::new()
610            .message(TextBlock::new_plain("This is\na message"))
611            .cause(cause.clone());
612        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
613
614        println!("{}", text);
615        assert_eq!(text, "\u{1b}[1;31m╭─▶ \u{1b}[0mThis is\n\u{1b}[1;31m│   \u{1b}[0ma message\n\u{1b}[1;31m├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber2\n\u{1b}[1;31m│  [4] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [3] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
616
617        // Traces without numbers
618        let log = StackBlock::new()
619            .add_stack_trace(
620                StackTraceBlock::new()
621                    .message(TextBlock::new_plain("This is a \n message"))
622                    .file_location(TextBlock::new_plain("/a/b/c"))
623                    .code_path(TextBlock::new_plain("crate::x")),
624            )
625            .add_stack_trace(
626                StackTraceBlock::new()
627                    .message(TextBlock::new_plain("This is a \n message2"))
628                    .file_location(TextBlock::new_plain("/a/b/c/2"))
629                    .code_path(TextBlock::new_plain("crate::x::2")),
630            )
631            .cause(cause.clone());
632        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
633
634        println!("{}", text);
635        assert_eq!(text, "\u{1b}[1;31m╭─ \n│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber2\n\u{1b}[1;31m│  [6] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [5] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
636
637        // Traces with numbers
638        let log = StackBlock::new()
639            .add_stack_trace(
640                StackTraceBlock::new()
641                    .message(TextBlock::new_plain("This is a \n message"))
642                    .file_location(TextBlock::new_plain("/a/b/c"))
643                    .code_path(TextBlock::new_plain("crate::x")),
644            )
645            .add_stack_trace(
646                StackTraceBlock::new()
647                    .message(TextBlock::new_plain("This is a \n message2"))
648                    .file_location(TextBlock::new_plain("/a/b/c/2"))
649                    .code_path(TextBlock::new_plain("crate::x::2")),
650            )
651            .show_stack_numbers(true)
652            .cause(cause.clone());
653        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
654
655        println!("{}", text);
656        assert_eq!(text, "\u{1b}[1;31m╭─ \n│  [2] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [1] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber2\n\u{1b}[1;31m│  [6] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [5] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
657
658        // All
659        let log = StackBlock::new()
660            .message(TextBlock::new_plain("This is\na message"))
661            .add_stack_trace(
662                StackTraceBlock::new()
663                    .message(TextBlock::new_plain("This is a \n message"))
664                    .file_location(TextBlock::new_plain("/a/b/c"))
665                    .code_path(TextBlock::new_plain("crate::x")),
666            )
667            .add_stack_trace(
668                StackTraceBlock::new()
669                    .message(TextBlock::new_plain("This is a \n message2"))
670                    .file_location(TextBlock::new_plain("/a/b/c/2"))
671                    .code_path(TextBlock::new_plain("crate::x::2")),
672            )
673            .show_stack_numbers(true)
674            .cause(cause.clone());
675        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
676
677        println!("{}", text);
678        assert_eq!(text, "\u{1b}[1;31m╭─▶ \u{1b}[0mThis is\n\u{1b}[1;31m│   \u{1b}[0ma message\n\u{1b}[1;31m│  [2] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [1] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Caused by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber2\n\u{1b}[1;31m│  [6] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [5] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
679    }
680
681    #[test]
682    fn test_styled_as_wrapped_by_format() {
683        let cause = StackBlock::new()
684            .message(TextBlock::new_plain("Cause\nnumber2"))
685            .add_stack_trace(
686                StackTraceBlock::new()
687                    .message(TextBlock::new_plain("This is a \n message"))
688                    .file_location(TextBlock::new_plain("/a/b/c"))
689                    .code_path(TextBlock::new_plain("crate::x")),
690            )
691            .add_stack_trace(
692                StackTraceBlock::new()
693                    .message(TextBlock::new_plain("This is a \n message2"))
694                    .file_location(TextBlock::new_plain("/a/b/c/2"))
695                    .code_path(TextBlock::new_plain("crate::x::2")),
696            )
697            .show_stack_numbers(true);
698        let cause = cause
699            .clone()
700            .message("Cause\nnumber1")
701            .show_stack_numbers(false)
702            .cause(cause.clone());
703
704        // Empty
705        let log = StackBlock::new()
706            .wrapped_by_format(true)
707            .cause(cause.clone());
708        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
709
710        println!("{}", text);
711        assert_eq!(text, "\u{1b}[1;31m╭─▶ \u{1b}[0mCause\n\u{1b}[1;31m│   \u{1b}[0mnumber2\n\u{1b}[1;31m│  [4] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [3] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \n╰─\u{1b}[0m");
712
713        // Message
714        let log = StackBlock::new()
715            .message(TextBlock::new_plain("This is\na message"))
716            .wrapped_by_format(true)
717            .cause(cause.clone());
718        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
719
720        println!("{}", text);
721        assert_eq!(text, "\u{1b}[1;31m╭─▶ \u{1b}[0mCause\n\u{1b}[1;31m│   \u{1b}[0mnumber2\n\u{1b}[1;31m│  [4] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [3] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \u{1b}[0mThis is\n\u{1b}[1;31m│     \u{1b}[0ma message\n\u{1b}[1;31m╰─\u{1b}[0m");
722
723        // Traces without numbers
724        let log = StackBlock::new()
725            .add_stack_trace(
726                StackTraceBlock::new()
727                    .message(TextBlock::new_plain("This is a \n message"))
728                    .file_location(TextBlock::new_plain("/a/b/c"))
729                    .code_path(TextBlock::new_plain("crate::x")),
730            )
731            .add_stack_trace(
732                StackTraceBlock::new()
733                    .message(TextBlock::new_plain("This is a \n message2"))
734                    .file_location(TextBlock::new_plain("/a/b/c/2"))
735                    .code_path(TextBlock::new_plain("crate::x::2")),
736            )
737            .wrapped_by_format(true)
738            .cause(cause.clone());
739        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
740
741        println!("{}", text);
742        assert_eq!(text, "\u{1b}[1;31m╭─▶ \u{1b}[0mCause\n\u{1b}[1;31m│   \u{1b}[0mnumber2\n\u{1b}[1;31m│  [6] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [5] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \n│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
743
744        // Traces with numbers
745        let log = StackBlock::new()
746            .add_stack_trace(
747                StackTraceBlock::new()
748                    .message(TextBlock::new_plain("This is a \n message"))
749                    .file_location(TextBlock::new_plain("/a/b/c"))
750                    .code_path(TextBlock::new_plain("crate::x")),
751            )
752            .add_stack_trace(
753                StackTraceBlock::new()
754                    .message(TextBlock::new_plain("This is a \n message2"))
755                    .file_location(TextBlock::new_plain("/a/b/c/2"))
756                    .code_path(TextBlock::new_plain("crate::x::2")),
757            )
758            .show_stack_numbers(true)
759            .wrapped_by_format(true)
760            .cause(cause.clone());
761        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
762
763        println!("{}", text);
764        assert_eq!(text, "\u{1b}[1;31m╭─▶ \u{1b}[0mCause\n\u{1b}[1;31m│   \u{1b}[0mnumber2\n\u{1b}[1;31m│  [6] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [5] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \n│  [2] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [1] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
765
766        // All
767        let log = StackBlock::new()
768            .message(TextBlock::new_plain("This is\na message"))
769            .add_stack_trace(
770                StackTraceBlock::new()
771                    .message(TextBlock::new_plain("This is a \n message"))
772                    .file_location(TextBlock::new_plain("/a/b/c"))
773                    .code_path(TextBlock::new_plain("crate::x")),
774            )
775            .add_stack_trace(
776                StackTraceBlock::new()
777                    .message(TextBlock::new_plain("This is a \n message2"))
778                    .file_location(TextBlock::new_plain("/a/b/c/2"))
779                    .code_path(TextBlock::new_plain("crate::x::2")),
780            )
781            .show_stack_numbers(true)
782            .wrapped_by_format(true)
783            .cause(cause.clone());
784        let text = log.print_to_string(LogLevel::error(), PrinterFormat::Styled);
785
786        println!("{}", text);
787        assert_eq!(text, "\u{1b}[1;31m╭─▶ \u{1b}[0mCause\n\u{1b}[1;31m│   \u{1b}[0mnumber2\n\u{1b}[1;31m│  [6] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [5] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \u{1b}[0mCause\n\u{1b}[1;31m│     \u{1b}[0mnumber1\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│   at \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m├───▶ Wrapped by: \u{1b}[0mThis is\n\u{1b}[1;31m│     \u{1b}[0ma message\n\u{1b}[1;31m│  [2] \u{1b}[0m/a/b/c\u{1b}[1;31m(\u{1b}[0mcrate::x\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message\n\u{1b}[1;31m│  [1] \u{1b}[0m/a/b/c/2\u{1b}[1;31m(\u{1b}[0mcrate::x::2\u{1b}[1;31m) - \u{1b}[0mThis is a \n\u{1b}[1;31m│     \u{1b}[0m message2\n\u{1b}[1;31m╰─\u{1b}[0m");
788    }
789}