errore/formatter.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
extern crate alloc;
use alloc::string::{String, ToString};
use core::fmt::{self, Debug};
use core::marker::{Send, Sync};
use crate::data::{Id, Metadata};
use crate::trace::{TraceAccess, TraceContext, TraceRecord};
/// A handler to format [`TraceRecord`] and [`TraceContext`] types.
#[allow(unused_mut)]
#[allow(unused_variables)]
pub trait Formatter: Sync + Send {
/// Formats a [`TraceRecord`] and passes it to the internal [`Display`](std::fmt::Display) trait implementation.
fn format_record(&self, rec: &TraceRecord, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"<{}> {} at {}",
rec.name,
rec.error_ref().map(|e| e.to_string()).unwrap_or_default(),
rec.location
)
}
/// Formats span related data and passes it to the internal [`Display`](std::fmt::Display) trait implementation.
fn format_span(
&self,
span: &(dyn Metadata + 'static),
ctx: &TraceContext,
f: &mut fmt::Formatter,
) -> fmt::Result {
write!(f, "{}: ", span.name())?;
span.display(f)?;
write!(f, "\n at {}", ctx.first().location)
}
/// Formats a [`TraceContext`] type and passes it to the internal [`Display`](std::fmt::Display) trait implementation.
fn format_trace(&self, ctx: &TraceContext, f: &mut fmt::Formatter) -> fmt::Result {
#[derive(Default)]
struct Fmt {
node: bool,
fmt_error: bool,
last_node: bool,
}
// Map trace records with additional data.
let mut last_id: Id = Id::default();
let trace = ctx.iter();
let mut nodes = trace
.map(|tr| {
let r = (
tr,
Fmt {
node: last_id != tr.id,
// Always format the first error and avoid duplicate messages,
// when using the transparent attribute.
fmt_error: last_id.is_null() || !tr.is_transparent,
last_node: false,
},
);
last_id = tr.id;
r
})
.collect::<alloc::vec::Vec<(&TraceRecord, Fmt)>>();
// Mark last node.
for (_, f) in nodes.iter_mut().rev() {
if f.node {
f.last_node = true;
break;
}
}
// Write the last emitted error.
// The error message is skipped because it is included in the trace itself.
writeln!(f, "Error: {}", ctx.last().name)?;
for i in 0..nodes.len() {
let (tr, n) = nodes.get(i).unwrap();
let next = nodes.get(i + 1);
let is_last = i == nodes.len() - 1;
let newline = if is_last { "" } else { "\n" };
// Switch direction symbols.
let lvl0_node;
let lvl0_continue;
let mut lvl1_node = "├";
let mut lvl0_newline = "";
if let Some(next) = next {
if n.last_node {
lvl0_node = "╰";
lvl0_continue = " ";
} else {
lvl0_node = "├";
lvl0_continue = "│";
}
if next.1.node {
lvl1_node = "╰";
lvl0_newline = "\n│";
}
} else {
lvl0_node = "╰";
lvl0_continue = " ";
lvl1_node = "╰";
}
// Write head node on first error or when the span type changes.
if n.node {
let node_msg = format!("{}─▶ <{}>", lvl0_node, tr.name);
write!(f, "{}", node_msg)?;
if n.fmt_error {
let error_msg = match tr.error_ref() {
Some(v) => v.to_string(),
None => String::new(),
};
#[cfg(feature = "std")]
{
// Indent message approximately to a width of 80 characters.
let msg = textwrap::wrap(
&error_msg,
textwrap::Options::new(70).subsequent_indent(&format!(
"│ │{}",
" ".repeat(node_msg.len().checked_sub(10).unwrap_or(0))
)),
);
// Write wrapped lines.
write!(f, " ")?;
for m in msg {
write!(f, "{}\n", m)?;
}
}
#[cfg(not(feature = "std"))]
{
write!(f, "{}\n", error_msg)?;
}
} else {
// write!(f, " [transparent]\n")?;
write!(f, "\n")?;
}
}
// Write location for every record.
write!(
f,
"{} {}╴ {}{}{}",
lvl0_continue, lvl1_node, tr.location, lvl0_newline, newline
)?;
}
fmt::Result::Ok(())
}
}
/// Default error formatter.
#[derive(Clone, Debug, Default)]
pub struct ErrorFormatter;
impl Formatter for ErrorFormatter {}