#![doc(html_logo_url = "https://raw.githubusercontent.com/mitsuhiko/deser/main/artwork/logo.svg")]
#![cfg_attr(not(test), no_std)]
extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use deser_core::ser::{Describe, Serialize, SerializeDriver, Variant, VariantKind, VariantRepr};
use deser_core::{Atom, Event};
pub struct ToDebug {
root: Node,
}
impl fmt::Display for ToDebug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl fmt::Debug for ToDebug {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&Render(&self.root, &self.root.desc), f)
}
}
impl ToDebug {
pub fn new(value: &dyn Serialize) -> ToDebug {
let mut builder = Builder::default();
SerializeDriver::new(value)
.drive_described(|event, value, state| {
builder.event(event, value, state.is_map_key());
Ok(())
})
.unwrap();
ToDebug {
root: builder.root.expect("no value was serialized"),
}
}
}
#[derive(Debug)]
enum Desc {
Structure(String),
Newtype(String),
TupleStruct(String),
UnitStruct(String),
Variant {
name: String,
kind: VariantKind,
repr: Repr,
},
Some,
None,
Tuple,
Set,
}
#[derive(Debug)]
enum Repr {
External,
Internal { tag: String },
Adjacent { content: String },
Untagged,
}
#[derive(Default)]
struct Collector(Vec<Desc>);
impl Describe for Collector {
fn structure(&mut self, name: &str) {
self.0.push(Desc::Structure(name.into()));
}
fn newtype(&mut self, name: &str) {
self.0.push(Desc::Newtype(name.into()));
}
fn tuple_struct(&mut self, name: &str) {
self.0.push(Desc::TupleStruct(name.into()));
}
fn unit_struct(&mut self, name: &str) {
self.0.push(Desc::UnitStruct(name.into()));
}
fn variant(&mut self, variant: &Variant<'_>) {
let repr = match variant.repr {
VariantRepr::Internal { tag } => Repr::Internal { tag: tag.into() },
VariantRepr::Adjacent { content, .. } => Repr::Adjacent {
content: content.into(),
},
VariantRepr::Untagged => Repr::Untagged,
_ => Repr::External,
};
self.0.push(Desc::Variant {
name: variant.name.into(),
kind: variant.kind,
repr,
});
}
fn some(&mut self) {
self.0.push(Desc::Some);
}
fn none(&mut self) {
self.0.push(Desc::None);
}
fn tuple(&mut self) {
self.0.push(Desc::Tuple);
}
fn set(&mut self) {
self.0.push(Desc::Set);
}
}
#[derive(Debug)]
struct Node {
desc: Vec<Desc>,
kind: NodeKind,
}
#[derive(Debug)]
enum NodeKind {
Atom(Atom<'static>),
Map(Vec<(Node, Node)>),
Seq(Vec<Node>),
}
impl Node {
fn get(&self, key: &str) -> Option<&Node> {
match self.kind {
NodeKind::Map(ref entries) => entries
.iter()
.find(|(k, _)| k.as_str() == Some(key))
.map(|(_, v)| v),
_ => None,
}
}
fn as_str(&self) -> Option<&str> {
match self.kind {
NodeKind::Atom(ref atom) => atom.as_str(),
_ => None,
}
}
}
#[derive(Default)]
struct Builder {
stack: Vec<Node>,
keys: Vec<Option<Node>>,
root: Option<Node>,
}
impl Builder {
fn event(&mut self, event: Event<'_>, value: &dyn Serialize, is_key: bool) {
let describe = |value: &dyn Serialize| {
let mut collector = Collector::default();
if !is_key {
value.describe(&mut collector);
}
collector.0
};
match event {
Event::Atom(atom) => {
let node = Node {
desc: describe(value),
kind: NodeKind::Atom(atom.to_static()),
};
self.push(node);
}
Event::MapStart(_) => {
self.stack.push(Node {
desc: describe(value),
kind: NodeKind::Map(Vec::new()),
});
self.keys.push(None);
}
Event::SeqStart(_) => {
self.stack.push(Node {
desc: describe(value),
kind: NodeKind::Seq(Vec::new()),
});
self.keys.push(None);
}
Event::MapEnd | Event::SeqEnd => {
self.keys.pop();
let node = self.stack.pop().expect("unbalanced events");
self.push(node);
}
}
}
fn push(&mut self, node: Node) {
let Some(parent) = self.stack.last_mut() else {
self.root = Some(node);
return;
};
match parent.kind {
NodeKind::Seq(ref mut items) => items.push(node),
NodeKind::Map(ref mut entries) => {
let key = self.keys.last_mut().unwrap();
match key.take() {
Some(key) => entries.push((key, node)),
None => *key = Some(node),
}
}
NodeKind::Atom(_) => unreachable!(),
}
}
}
struct Render<'a>(&'a Node, &'a [Desc]);
impl<'a> Render<'a> {
fn plain(node: &'a Node) -> Render<'a> {
Render(node, &node.desc)
}
}
impl fmt::Debug for Render<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Render(node, desc) = *self;
let Some((first, rest)) = desc.split_first() else {
return render_undescribed(node, f);
};
match *first {
Desc::Some => f.debug_tuple("Some").field(&Render(node, rest)).finish(),
Desc::None => f.write_str("None"),
Desc::Newtype(ref name) => f.debug_tuple(name).field(&Render(node, rest)).finish(),
Desc::Structure(ref name) => render_struct(name, node, None, f),
Desc::TupleStruct(ref name) => render_tuple(name, node, f),
Desc::UnitStruct(ref name) => f.write_str(name),
Desc::Tuple => render_tuple("", node, f),
Desc::Set => match node.kind {
NodeKind::Seq(ref items) => f
.debug_set()
.entries(items.iter().map(Render::plain))
.finish(),
_ => render_undescribed(node, f),
},
Desc::Variant {
ref name,
kind,
ref repr,
} => render_variant(name, kind, repr, node, rest, f),
}
}
}
fn render_undescribed(node: &Node, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match node.kind {
NodeKind::Atom(ref atom) => render_atom(atom, f),
NodeKind::Map(ref entries) => f
.debug_map()
.entries(
entries
.iter()
.map(|(k, v)| (Render::plain(k), Render::plain(v))),
)
.finish(),
NodeKind::Seq(ref items) => f
.debug_list()
.entries(items.iter().map(Render::plain))
.finish(),
}
}
fn render_atom(atom: &Atom, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *atom {
Atom::Null => f.write_str("()"),
Atom::Bool(v) => fmt::Debug::fmt(&v, f),
Atom::Str(ref v) | Atom::Lexical(ref v) => fmt::Debug::fmt(v, f),
Atom::Bytes(ref v) => fmt::Debug::fmt(v.data(), f),
Atom::Char(v) => fmt::Debug::fmt(&v, f),
Atom::U64(v) => fmt::Debug::fmt(&v, f),
Atom::I64(v) => fmt::Debug::fmt(&v, f),
Atom::F32(v) => fmt::Debug::fmt(&v, f),
Atom::F64(v) => fmt::Debug::fmt(&v, f),
Atom::Ext(ref v) => fmt::Debug::fmt(v, f),
Atom::Implicit(ref v) => render_atom(&v.value().to_atom(), f),
_ => f.write_str("?"),
}
}
fn render_struct(
name: &str,
node: &Node,
skip: Option<&str>,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let NodeKind::Map(ref entries) = node.kind else {
return render_undescribed(node, f);
};
let mut s = f.debug_struct(name);
for (key, value) in entries {
match key.as_str() {
Some(key) if Some(key) == skip => {}
Some(key) => {
s.field(key, &Render::plain(value));
}
None => {
s.field("?", &Render::plain(value));
}
}
}
s.finish()
}
struct StructWithout<'a>(&'a str, &'a Node, &'a str);
impl fmt::Debug for StructWithout<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
render_struct(self.0, self.1, Some(self.2), f)
}
}
fn render_tuple(name: &str, node: &Node, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let NodeKind::Seq(ref items) = node.kind else {
return render_undescribed(node, f);
};
let mut t = f.debug_tuple(name);
for item in items {
t.field(&Render::plain(item));
}
t.finish()
}
fn render_content(
name: &str,
kind: VariantKind,
content: &Node,
rest: &[Desc],
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match kind {
VariantKind::Unit => f.write_str(name),
VariantKind::Newtype => f.debug_tuple(name).field(&Render(content, rest)).finish(),
VariantKind::Tuple => render_tuple(name, content, f),
_ => render_struct(name, content, None, f),
}
}
fn render_variant(
name: &str,
kind: VariantKind,
repr: &Repr,
node: &Node,
rest: &[Desc],
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
if kind == VariantKind::Unit {
return f.write_str(name);
}
match *repr {
Repr::External => match node.kind {
NodeKind::Map(ref entries) if entries.len() == 1 => {
let content = &entries[0].1;
render_content(name, kind, content, &content.desc, f)
}
_ => render_undescribed(node, f),
},
Repr::Internal { ref tag } => match (kind, rest) {
(VariantKind::Newtype, [Desc::Structure(inner), ..]) => f
.debug_tuple(name)
.field(&StructWithout(inner, node, tag))
.finish(),
(VariantKind::Newtype | VariantKind::Struct, _) => {
render_struct(name, node, Some(tag), f)
}
_ => render_undescribed(node, f),
},
Repr::Adjacent { ref content } => match node.get(content) {
Some(content) => render_content(name, kind, content, &content.desc, f),
None => render_undescribed(node, f),
},
Repr::Untagged => render_content(name, kind, node, rest, f),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_debug_format() {
let mut m = alloc::collections::BTreeMap::new();
m.insert(true, vec![vec![&b"x"[..], b"yyy"], vec![b"zzzz\x00\x01"]]);
m.insert(false, vec![]);
assert_eq!(ToDebug::new(&m).to_string(), format!("{:?}", m));
}
#[test]
fn test_debug_format_ext() {
assert_eq!(
ToDebug::new(&vec![u128::MAX, 1]).to_string(),
format!("[{}, 1]", u128::MAX)
);
}
#[test]
fn test_std_types() {
fn check<T: Serialize + fmt::Debug>(value: T) {
assert_eq!(ToDebug::new(&value).to_string(), format!("{:?}", value));
assert_eq!(
format!("{:#?}", ToDebug::new(&value)),
format!("{:#?}", value)
);
}
check(42u32);
check("hello");
check('x');
check(Some(Some(1i64)));
check(None::<u32>);
check((1, "two", Some(3.5)));
check(vec![Some(1), None]);
check(alloc::collections::BTreeSet::from([1, 2, 3]));
check(alloc::collections::BTreeMap::from([("a", (1, 2))]));
check(Box::new(Some(vec![true])));
check(());
}
}