use {
crate::color::{
ColorGenerator,
RGB,
},
core::fmt,
facet::{
Facet,
GenericPtr,
StructKind,
},
facet_reflect::*,
};
use crate::facet_pretty;
#[derive(Copy, Clone, Debug)]
pub struct Display<'mem, 'facet> {
this: DiffPrinter,
report: Peek<'mem, 'facet>,
lhs: Peek<'mem, 'facet>,
rhs: Peek<'mem, 'facet>,
}
impl fmt::Display for Display<'_, '_> {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
self.this
.write_report_impl(f, self.report, self.lhs, self.rhs)
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Indent(u32);
impl Indent {
const MASK: u32 = u32::MAX >> 1;
pub fn tabs(width: u32) -> Self {
let width = Ord::min(width, Self::MASK);
Self(width | !Self::MASK)
}
pub fn spaces(width: u32) -> Self {
let width = Ord::min(width, Self::MASK);
Self(width)
}
pub fn width(self) -> u32 {
self.0 & Self::MASK
}
pub fn is_tabs(self) -> bool {
self.0 & !Self::MASK != 0
}
pub fn is_spaces(self) -> bool {
!self.is_tabs()
}
pub fn write(
self,
f: &mut dyn fmt::Write,
level: usize,
) -> fmt::Result {
for _ in 0..level {
write!(f, "{self}")?;
}
Ok(())
}
pub(crate) fn writeln(
self,
f: &mut fmt::Formatter,
level: usize,
) -> fmt::Result {
writeln!(f)?;
self.write(f, level)
}
}
impl Default for Indent {
fn default() -> Self {
Self::tabs(1)
}
}
impl fmt::Debug for Indent {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let width = self.width();
if self.is_tabs() {
write!(f, "{width}×TAB")
} else {
write!(f, "{width}×SPACE")
}
}
}
impl fmt::Display for Indent {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let width = self.width() as usize;
if self.is_tabs() {
write!(f, "{: <width$}", "", width = width)
} else {
write!(f, "{: <width$}", "", width = width)
}
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Format {
pub indent: Indent,
pub color: ColorGenerator,
}
#[derive(Copy, Clone, Debug, Default)]
pub struct DiffPrinter(pub Format);
#[derive(Facet)]
struct Nil;
enum StackDiffState<'mem, 'facet> {
Start {
report: Peek<'mem, 'facet>,
lhs: Peek<'mem, 'facet>,
rhs: Peek<'mem, 'facet>,
},
Struct {
report: FieldIter<'mem, 'facet>,
lhs: PeekStruct<'mem, 'facet>,
rhs: PeekStruct<'mem, 'facet>,
close: Option<char>,
},
Enum {
report: FieldIter<'mem, 'facet>,
lhs: PeekEnum<'mem, 'facet>,
rhs: PeekEnum<'mem, 'facet>,
close: Option<char>,
},
List {
report: PeekListLikeIter<'mem, 'facet>,
lhs: PeekListLike<'mem, 'facet>,
rhs: PeekListLike<'mem, 'facet>,
},
Map {
report: PeekListLikeIter<'mem, 'facet>,
lhs: Peek<'mem, 'facet>,
rhs: Peek<'mem, 'facet>,
},
}
struct StackDiffItem<'mem, 'facet> {
format_depth: usize,
type_depth: usize,
state: StackDiffState<'mem, 'facet>,
}
impl Format {
fn printer(
self,
indent_level: usize,
) -> facet_pretty::PrettyPrinter {
facet_pretty::PrettyPrinter::new()
.with_indent_level(indent_level)
.with_indent_size(self.indent)
.with_colors(!self.color.is_none())
}
}
impl DiffPrinter {
fn write_report_impl<'facet>(
&self,
f: &mut fmt::Formatter,
report: Peek<'_, 'facet>,
lhs: Peek<'_, 'facet>,
rhs: Peek<'_, 'facet>,
) -> fmt::Result {
let mut stack = Vec::new();
stack.push(StackDiffItem {
format_depth: 0,
type_depth: 0,
state: StackDiffState::Start { report, lhs, rhs },
});
'stack: while let Some(item) = stack.pop() {
let color = self
.0
.color
.generate_color_for(&((item.format_depth + 2).wrapping_mul(0xACAB)));
match item.state {
StackDiffState::Start { report, lhs, rhs } => {
'give_up: {
if report.shape().type_identifier == "Mismatch"
&& let Ok(report) = report.into_enum()
&& let &[unrelated, reports] = report.variants()
&& unrelated.name == "Unrelated"
&& reports.name == "Report"
&& let Ok(index) = report.variant_index()
{
if index == 1
&& let &[_] = reports.data.fields
&& let Ok(Some(report)) = report.field(0)
{
if let Ok(report) = report.into_list_like()
&& let facet::Type::User(facet::UserType::Struct(report_at)) =
report.def().t().ty && let &[loc_field, report_field] =
report_at.fields && loc_field.name == "loc"
&& report_field.name == "report"
{
let ty = lhs;
if let Ok(lhs) = lhs.into_list_like()
&& let Ok(rhs) = rhs.into_list_like()
{
color.write_fg(f)?;
self.0.printer(item.format_depth).write_type_name(f, ty)?;
color.reset(f)?;
write!(f, "{}", color.fg(" ["))?;
stack.push(StackDiffItem {
format_depth: item.format_depth,
type_depth: item.type_depth + 1,
state: StackDiffState::List {
report: report.iter(),
lhs,
rhs,
},
});
continue 'stack;
}
if let Ok(_) = lhs.into_map()
&& let Ok(_) = rhs.into_map()
{
color.write_fg(f)?;
self.0.printer(item.format_depth).write_type_name(f, ty)?;
color.reset(f)?;
write!(f, "{}", color.fg(" ["))?;
stack.push(StackDiffItem {
format_depth: item.format_depth,
type_depth: item.type_depth + 1,
state: StackDiffState::Map {
report: report.iter(),
lhs,
rhs,
},
});
continue 'stack;
}
break 'give_up;
}
let ty = lhs;
if let Ok(report) = report.into_struct()
&& let Ok(lhs) = lhs.into_struct()
&& let Ok(rhs) = rhs.into_struct()
{
let kind = lhs.ty().kind;
if !matches!(kind, facet::StructKind::Tuple) {
color.write_fg(f)?;
self.0.printer(item.format_depth).write_type_name(f, ty)?;
color.reset(f)?;
}
let close = match kind {
StructKind::Unit => None,
StructKind::TupleStruct | StructKind::Tuple => {
write!(f, "{}", color.fg("("))?;
Some(')')
},
StructKind::Struct => {
write!(f, "{}", color.fg("{"))?;
Some('}')
},
};
stack.push(StackDiffItem {
format_depth: item.format_depth,
type_depth: item.type_depth + 1,
state: StackDiffState::Struct {
report: report.fields(),
lhs,
rhs,
close,
},
});
}
if let Ok(report) = report.into_enum()
&& let Ok(lhs) = lhs.into_enum()
&& let Ok(rhs) = rhs.into_enum()
&& let Ok(report_v) = report.active_variant()
&& let Ok(lhs_v) = lhs.active_variant()
&& let Ok(rhs_v) = rhs.active_variant()
&& report_v.name == lhs_v.name
&& report_v.name == rhs_v.name
{
color.write_fg(f)?;
self.0.printer(item.format_depth).write_type_name(f, ty)?;
color.reset(f)?;
write!(f, "::{} ", lhs_v.name)?;
let close = match lhs_v.data.kind {
StructKind::Unit => None,
StructKind::TupleStruct | StructKind::Tuple => {
write!(f, "{}", color.fg("("))?;
Some(')')
},
StructKind::Struct => {
write!(f, "{}", color.fg("{"))?;
Some('}')
},
};
stack.push(StackDiffItem {
format_depth: item.format_depth,
type_depth: item.type_depth + 1,
state: StackDiffState::Enum {
report: report.fields(),
lhs,
rhs,
close,
},
});
}
break 'give_up;
}
}
};
let printer = self.0.printer(item.format_depth);
let printer = printer.with_color_generator(
facet_pretty::ColorGenerator::new().with_base_hue(300.0),
);
if lhs.shape().id.get() != Nil::SHAPE.id.get() {
self.0.indent.writeln(f, item.format_depth)?;
let color = self.0.color.map_color(RGB::hex(0xFF6363));
color.write_bold(f)?;
write!(f, " {} : ", color.fg("-"))?;
printer.format_peek_to(lhs, f)?;
if item.format_depth > 0 {
write!(f, ",")?;
}
}
if rhs.shape().id.get() != Nil::SHAPE.id.get() {
self.0.indent.writeln(f, item.format_depth)?;
let color = self.0.color.map_color(RGB::hex(0x03A791));
color.write_bold(f)?;
write!(f, " {} : ", color.fg("+"))?;
let printer = printer.with_color_generator(
facet_pretty::ColorGenerator::new().with_base_hue(210.0),
);
printer.format_peek_to(rhs, f)?;
if item.format_depth > 0 {
write!(f, ",")?;
}
}
},
StackDiffState::List {
mut report,
lhs,
rhs,
} => {
if let Some(report_item) = report.next() {
stack.push(StackDiffItem {
format_depth: item.format_depth,
type_depth: item.type_depth,
state: StackDiffState::List { report, lhs, rhs },
});
if let Ok(report_item) = report_item.into_struct()
&& let Ok(loc) = report_item.field(0)
&& let Ok(report_item) = report_item.field(1)
&& let Ok(&loc) = loc.get::<usize>()
{
let lhs_item = lhs.get(loc).unwrap_or(Peek::new(const { &Nil }));
let rhs_item = rhs.get(loc).unwrap_or(Peek::new(const { &Nil }));
self.0.indent.writeln(f, item.format_depth + 1)?;
color.write_fg(f)?;
color.write_bold(f)?;
write!(f, "[{loc}]: ")?;
color.reset(f)?;
stack.push(StackDiffItem {
format_depth: item.format_depth + 1,
type_depth: item.type_depth + 1,
state: StackDiffState::Start {
report: report_item,
lhs: lhs_item,
rhs: rhs_item,
},
});
}
} else {
self.0.indent.writeln(f, item.format_depth)?;
write!(f, "{}", color.fg("]"))?;
if item.format_depth > 0 {
write!(f, ",")?;
}
}
},
StackDiffState::Map {
mut report,
lhs,
rhs,
} => {
if let Some(report_item) = report.next() {
stack.push(StackDiffItem {
format_depth: item.format_depth,
type_depth: item.type_depth,
state: StackDiffState::Map { report, lhs, rhs },
});
if let Ok(report_item) = report_item.into_struct()
&& let Ok(loc) = report_item.field(0)
&& let Ok(report_item) = report_item.field(1)
&& let GenericPtr::Thin(loc_ptr) = loc.data()
&& let GenericPtr::Thin(lhs_ptr) = lhs.data()
&& let GenericPtr::Thin(rhs_ptr) = rhs.data()
&& let Ok(lhs_map) = lhs.into_map()
&& let Ok(rhs_map) = rhs.into_map()
{
self.0.indent.writeln(f, item.format_depth + 1)?;
if loc.vtable().has_display() {
color.write_fg(f)?;
color.write_bold(f)?;
write!(f, "[")?;
match loc.vtable() {
facet::ValueVTable::Sized(vtable) => unsafe {
(vtable.display)().unwrap()(loc_ptr, f)?;
},
facet::ValueVTable::Unsized(_) => {
unreachable!()
},
}
write!(f, "]")?;
color.reset(f)?;
} else {
let printer = self.0.printer(item.format_depth + 1);
let printer = printer.with_color_generator(
facet_pretty::ColorGenerator::new().with_base_hue(150.0),
);
printer.format_peek_to(loc, f)?;
}
f.write_str(": ")?;
let lhs_item = unsafe {
(lhs_map.def().vtable.get_value_ptr_fn)(lhs_ptr, loc_ptr)
};
let rhs_item = unsafe {
(rhs_map.def().vtable.get_value_ptr_fn)(rhs_ptr, loc_ptr)
};
let lhs_item = lhs_item
.map(|ptr| unsafe { Peek::unchecked_new(ptr, lhs_map.def().v()) });
let rhs_item = rhs_item
.map(|ptr| unsafe { Peek::unchecked_new(ptr, rhs_map.def().v()) });
let lhs_item = lhs_item.unwrap_or(Peek::new(const { &Nil }));
let rhs_item = rhs_item.unwrap_or(Peek::new(const { &Nil }));
stack.push(StackDiffItem {
format_depth: item.format_depth + 1,
type_depth: item.type_depth + 1,
state: StackDiffState::Start {
report: report_item,
lhs: lhs_item,
rhs: rhs_item,
},
});
}
} else {
self.0.indent.writeln(f, item.format_depth)?;
write!(f, "{}", color.fg("]"))?;
if item.format_depth > 0 {
write!(f, ",")?;
}
}
},
StackDiffState::Struct {
mut report,
lhs,
rhs,
close,
} => {
if let Some((field, report_field)) = report.next() {
stack.push(StackDiffItem {
format_depth: item.format_depth,
type_depth: item.type_depth,
state: StackDiffState::Struct {
report,
lhs,
rhs,
close,
},
});
if let Some(index) =
lhs.ty().fields.iter().position(|f| f.name == field.name)
&& let Ok(lhs) = lhs.field(index)
&& let Ok(rhs) = rhs.field(index)
{
stack.push(StackDiffItem {
format_depth: item.format_depth + 1,
type_depth: item.type_depth + 1,
state: StackDiffState::Start {
report: report_field,
lhs,
rhs,
},
});
}
} else {
if let Some(close) = close {
self.0.indent.writeln(f, item.format_depth)?;
write!(f, "{}", color.fg(&close))?;
}
if item.format_depth > 0 {
write!(f, ",")?;
}
}
},
StackDiffState::Enum {
mut report,
lhs,
rhs,
close,
} => {
if let Some((field, report_field)) = report.next() {
stack.push(StackDiffItem {
format_depth: item.format_depth,
type_depth: item.type_depth,
state: StackDiffState::Enum {
report,
lhs,
rhs,
close,
},
});
if let Ok(active) = lhs.active_variant()
&& let Some(index) =
active.data.fields.iter().position(|f| f.name == field.name)
&& let Ok(Some(lhs)) = lhs.field(index)
&& let Ok(Some(rhs)) = rhs.field(index)
{
stack.push(StackDiffItem {
format_depth: item.format_depth + 1,
type_depth: item.type_depth + 1,
state: StackDiffState::Start {
report: report_field,
lhs,
rhs,
},
});
}
} else {
if let Some(close) = close {
self.0.indent.writeln(f, item.format_depth)?;
write!(f, "{}", color.fg(&close))?;
}
if item.format_depth > 0 {
write!(f, ",")?;
}
}
},
}
}
Ok(())
}
pub fn format_peek_to<'facet>(
&self,
f: &mut dyn fmt::Write,
report: Peek<'_, 'facet>,
lhs: Peek<'_, 'facet>,
rhs: Peek<'_, 'facet>,
) -> fmt::Result {
write!(
f,
"{}",
Display {
this: *self,
report,
lhs,
rhs
}
)
}
pub fn peek_as_format<'mem, 'facet>(
&self,
report: Peek<'mem, 'facet>,
lhs: Peek<'mem, 'facet>,
rhs: Peek<'mem, 'facet>,
) -> Display<'mem, 'facet> {
Display {
this: *self,
report,
lhs,
rhs,
}
}
pub fn format_to<'facet, T: ?Sized + Facet<'facet>, R: ?Sized + Facet<'facet>>(
&self,
f: &mut dyn fmt::Write,
report: &R,
lhs: &T,
rhs: &T,
) -> fmt::Result {
self.format_peek_to(f, Peek::new(report), Peek::new(lhs), Peek::new(rhs))
}
pub fn format<'facet, T: ?Sized + Facet<'facet>, R: ?Sized + Facet<'facet>>(
&self,
report: &R,
lhs: &T,
rhs: &T,
) -> String {
let mut out = String::new();
self.format_to(&mut out, report, lhs, rhs).unwrap();
out
}
pub fn as_format<'mem, 'facet, T: ?Sized + Facet<'facet>, R: ?Sized + Facet<'facet>>(
&self,
report: &'mem R,
lhs: &'mem T,
rhs: &'mem T,
) -> Display<'mem, 'facet> {
self.peek_as_format(Peek::new(report), Peek::new(lhs), Peek::new(rhs))
}
}