diff --git a/src/data/ext.rs b/src/data/ext.rs index 47db0f0..53736fe 100644 --- a/src/data/ext.rs +++ b/src/data/ext.rs @@ -224,7 +224,7 @@ pub trait DataExt: Data { #[allow(unused_variables)] #[inline] #[must_use] - fn format(&self) -> format::FormattableData { + fn format(&self) -> format::FormattableData { self.into() } } diff --git a/src/data/format.rs b/src/data/format.rs index d882b1f..d51bb05 100644 --- a/src/data/format.rs +++ b/src/data/format.rs @@ -4,19 +4,27 @@ use std::{ marker::PhantomData, }; -use crate::data::{BoxedData, Data}; use crate::links::{Links, Result, CONTINUE}; use crate::value::ValueBuiler; +use crate::{ + data::{BoxedData, Data}, + links::Link, +}; #[derive(Default, Debug)] -pub struct FORMAT; +pub struct FORMAT; -pub type COMPACT = FORMAT; -pub type DEBUG = FORMAT; +pub type COMPACT = FORMAT; +pub type DEBUG = FORMAT; -pub trait DataFormatter { +pub trait Format { type State: Default + Copy; + #[inline] + fn verbosity() -> i8 { + 0 + } + #[inline] #[must_use] fn init_state() -> Self::State { @@ -29,181 +37,157 @@ pub trait DataFormatter { data: &(impl Data + ?Sized), state: Self::State, ) -> fmt::Result { - let mut d = Self::create_debug_struct(f, data, state); + // Format prefix + Self::fmt_prefix(f, data)?; + + if Self::verbosity() < 0 { + let values = crate::value::Value::from_data(data); + if let Some(val) = values.as_enum() { + let mut has_links = None::; + // Ignore errors + let _ = data.provide_links(&mut has_links); + if has_links.is_none() { + if let Some(v) = val { + f.write_fmt(format_args!("({v})"))?; + } else { + f.write_str("")?; + } + return Ok(()); + } + } + } + + let mut set = f.debug_set(); // Format values - Self::fmt_values(&mut d, data, state); + Self::fmt_values_into_set(&mut set, data, state); // Format links - Self::fmt_links(&mut d, data, state); + Self::fmt_links_into_set(&mut set, data, state); + + // Finish set + set.finish()?; - d.finish() + // Format suffix + Self::fmt_suffix(f, data)?; + Ok(()) } - #[allow(unused_variables)] #[inline] - fn fmt_values( - f: &mut fmt::DebugStruct<'_, '_>, - data: &(impl Data + ?Sized), - state: Self::State, - ) { - data.provide_value(f); + fn fmt_prefix(f: &mut fmt::Formatter<'_>, data: &(impl Data + ?Sized)) -> fmt::Result { + if Self::verbosity() <= -2 { + f.write_str("D")?; + } else { + f.write_str("Data")?; + } + + #[cfg(feature = "unique")] + if Self::verbosity() > 0 { + if let Some(id) = data.get_id() { + f.write_fmt(format_args!("[{id}]"))?; + } + } + + Ok(()) } #[allow(unused_variables)] #[inline] - fn fmt_links( - f: &mut fmt::DebugStruct<'_, '_>, - data: &(impl Data + ?Sized), - state: Self::State, - ) { - f.field("links", &format_args!("...")); + fn fmt_suffix(f: &mut fmt::Formatter<'_>, data: &(impl Data + ?Sized)) -> fmt::Result { + Ok(()) } #[inline] - fn fmt_link( - f: &mut fmt::Formatter<'_>, - key: Option<&(impl Data + ?Sized)>, - target: &(impl Data + ?Sized), + fn fmt_link<'a, 'b, L: Link>( + f: &'a mut fmt::Formatter<'b>, + link: &L, state: Self::State, ) -> fmt::Result { - if let Some(key) = key { + if let Some(key) = link.key() { Self::fmt(f, key, state)?; f.write_str(" -> ")?; } else { f.write_str("- ")?; } - Self::fmt(f, target, state)?; - - Ok(()) + Self::fmt(f, link.target(), state) } #[allow(unused_variables)] #[inline] - fn create_debug_struct<'a, 'b>( - f: &'a mut fmt::Formatter<'b>, + fn fmt_values_into_set<'a, 'b>( + set: &mut fmt::DebugSet<'a, 'b>, data: &(impl Data + ?Sized), state: Self::State, - ) -> fmt::DebugStruct<'a, 'b> { - #[cfg(feature = "unique")] - if let Some(id) = data.get_id() { - return f.debug_struct(&format!("Data[{id}]")); - } - - f.debug_struct("Data") - } -} - -impl DataFormatter for FORMAT { - type State = u16; - - #[inline] - fn init_state() -> Self::State { - MAX_DEPTH + ) { + data.provide_value(set); } + #[allow(unused_variables)] #[inline] - fn fmt_links( - f: &mut fmt::DebugStruct<'_, '_>, + fn fmt_links_into_set<'a, 'b>( + set: &mut fmt::DebugSet<'a, 'b>, data: &(impl Data + ?Sized), state: Self::State, ) { - if MAX_DEPTH == 0 || state == 0 { - f.field("links", &format_args!("...")); - return; - } - - if SERIAL { - let links = serial::SerialLinks::::new(data, state.saturating_sub(1)); - if !links.is_empty() { - f.field("links", &links); - } - return; - } - - let links = recursive::RecursiveFmt::::new(data, state.saturating_sub(1)); - f.field("links", &links); + set.entry(&format_args!("...")); } } -impl DataFormatter for FORMAT { +impl Format + for FORMAT +{ type State = u16; #[inline] - fn init_state() -> Self::State { - MAX_DEPTH + fn verbosity() -> i8 { + VERBOSITY } #[inline] - fn fmt( - f: &mut fmt::Formatter<'_>, - data: &(impl Data + ?Sized), - state: Self::State, - ) -> fmt::Result { - let values = crate::value::Value::from_data(data); - let links = serial::SerialLinks::::new(data, state.saturating_sub(1)); - - if links.is_empty() { - match values.as_enum() { - Some(None) => { - f.write_str("Data")?; - return Ok(()); - } - Some(Some(v)) => { - f.write_fmt(format_args!("Data({v})"))?; - return Ok(()); - } - None => {} - } - } - - let mut d = f.debug_struct("Data"); - - // Format values - Self::fmt_values(&mut d, &values, state); - - // Format links - if links.is_empty() { - // no links - } else if MAX_DEPTH == 0 || state == 0 { - d.field("links", &format_args!("...")); - } else { - d.field("links", &links); - } - - d.finish() + fn init_state() -> Self::State { + MAX_DEPTH } #[inline] - fn fmt_links( - f: &mut fmt::DebugStruct<'_, '_>, + fn fmt_links_into_set<'a, 'b>( + set: &mut fmt::DebugSet<'a, 'b>, data: &(impl Data + ?Sized), state: Self::State, ) { if MAX_DEPTH == 0 || state == 0 { - f.field("links", &format_args!("...")); + set.entry(&format_args!("...")); return; } if SERIAL { - let links = serial::SerialLinks::::new(data, state - 1); - if !links.is_empty() { - f.field("links", &links); - } - return; + let mut links = Vec::<(Option, BoxedData)>::new(); + // Ignore errors + let _ = data.provide_links(&mut links); + let inner_state = state.saturating_sub(1); + set.entries(links.into_iter().map(|(key, target)| { + let link = LinkEntry::<_, Self> { + link: (key, target), + state: inner_state, + }; + link + })); + } else { + let mut links = StreamingLinks::<'_, '_, '_, Self> { + fmt_set: set, + state: state.saturating_sub(1), + }; + // Ignore errors + let _ = data.provide_links(&mut links); } - - let links = recursive::RecursiveFmt::::new(data, state - 1); - f.field("links", &links); } } -pub struct FormattableData<'d, F: DataFormatter, D: Data + ?Sized> { +pub struct FormattableData<'d, F: Format, D: Data + ?Sized> { data: &'d D, phantom: PhantomData, } -impl<'d, F: DataFormatter, D: Data + ?Sized> From<&'d D> for FormattableData<'d, F, D> { +impl<'d, F: Format, D: Data + ?Sized> From<&'d D> for FormattableData<'d, F, D> { #[inline] fn from(data: &'d D) -> Self { Self { @@ -213,201 +197,115 @@ impl<'d, F: DataFormatter, D: Data + ?Sized> From<&'d D> for FormattableData<'d, } } -impl Display for FormattableData<'_, F, D> { +impl Display for FormattableData<'_, F, D> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { F::fmt(f, self.data, F::init_state()) } } -impl Debug for FormattableData<'_, F, D> { +impl Debug for FormattableData<'_, F, D> { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { F::fmt(f, self.data, F::init_state()) } } -struct FormattableLink { - key: Option, - target: T, +struct LinkEntry { + link: L, state: F::State, - phantom: PhantomData, } -impl Debug for FormattableLink { +impl Debug for LinkEntry { + #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - F::fmt_link(f, self.key.as_ref(), &self.target, self.state) + F::fmt_link(f, &self.link, self.state) } } -mod recursive { - use super::*; - - pub(super) struct RecursiveFmt<'d, F: DataFormatter, D: Data + ?Sized> { - data: &'d D, - state: F::State, - phantom: PhantomData, - } - - impl<'d, F: DataFormatter, D: Data + ?Sized> RecursiveFmt<'d, F, D> { - #[inline] - pub(super) fn new(data: &'d D, state: F::State) -> Self { - Self { - data, - state, - phantom: PhantomData, - } - } - } - - impl<'d, F: DataFormatter, D: Data + ?Sized> Debug for RecursiveFmt<'d, F, D> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let fmt_set = f.debug_set(); - let mut links = RecursiveLinks:: { - fmt_set, - state: self.state, - phantom: PhantomData, - }; - self.data - .provide_links(&mut links) - .map_err(|_| std::fmt::Error)?; - Ok(()) - } - } - - struct RecursiveLinks<'a, 'b, F: DataFormatter> { - fmt_set: fmt::DebugSet<'a, 'b>, - state: F::State, - phantom: PhantomData, - } - - impl Links for RecursiveLinks<'_, '_, F> { - fn push(&mut self, target: BoxedData, key: Option) -> Result { - let link = FormattableLink:: { - key, - target, - state: self.state, - phantom: PhantomData, - }; - self.fmt_set.entry(&link); - - CONTINUE - } - } +struct StreamingLinks<'a, 'b, 'c, F: Format> { + fmt_set: &'a mut fmt::DebugSet<'b, 'c>, + state: F::State, } -mod serial { - - use super::*; - - pub(super) struct SerialLinks<'d, F: DataFormatter, D: Data + ?Sized> { - state: F::State, - links: Vec<(Option, BoxedData)>, - formatter: PhantomData, - data: PhantomData<&'d D>, - } - - impl<'d, F: DataFormatter, D: Data + ?Sized> SerialLinks<'d, F, D> { - #[inline] - pub(super) fn new(data: &'d D, state: F::State) -> Self { - let mut links = Vec::<(Option, BoxedData)>::new(); - // TODO: do something about an error here - let _ = data.provide_links(&mut links); - Self { - state, - links, - formatter: PhantomData, - data: PhantomData, - } - } - - #[inline] - pub(super) fn is_empty(&self) -> bool { - self.links.is_empty() - } - } +impl Links for StreamingLinks<'_, '_, '_, F> { + fn push(&mut self, target: BoxedData, key: Option) -> Result { + let link = LinkEntry::<_, F> { + link: (key, target), + state: self.state, + }; + self.fmt_set.entry(&link); - impl<'d, F: DataFormatter, D: Data + ?Sized> Debug for SerialLinks<'d, F, D> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut fmt_set = f.debug_set(); - for (k, t) in &self.links { - let link = FormattableLink:: { - key: k.as_ref().map(|k| &**k), - target: t.as_ref(), - state: self.state, - phantom: PhantomData, - }; - fmt_set.entry(&link); - } - fmt_set.finish() - } + CONTINUE } } -#[warn(clippy::missing_trait_methods)] -impl ValueBuiler<'_> for fmt::DebugStruct<'_, '_> { +impl ValueBuiler<'_> for fmt::DebugSet<'_, '_> { #[inline] fn bool(&mut self, value: bool) { - self.field("bool", &value); + match value { + true => self.entry(&"bool: true"), + false => self.entry(&"bool: false"), + }; } #[inline] fn u8(&mut self, value: u8) { - self.field("u8", &value); + self.entry(&format_args!("u8: {}", value)); } #[inline] fn i8(&mut self, value: i8) { - self.field("i8", &value); + self.entry(&format_args!("i8: {}", value)); } #[inline] fn u16(&mut self, value: u16) { - self.field("u16", &value); + self.entry(&format_args!("u16: {}", value)); } #[inline] fn i16(&mut self, value: i16) { - self.field("i16", &value); + self.entry(&format_args!("i16: {}", value)); } #[inline] fn u32(&mut self, value: u32) { - self.field("u32", &value); + self.entry(&format_args!("u32: {}", value)); } #[inline] fn i32(&mut self, value: i32) { - self.field("i32", &value); + self.entry(&format_args!("i32: {}", value)); } #[inline] fn u64(&mut self, value: u64) { - self.field("u64", &value); + self.entry(&format_args!("u64: {}", value)); } #[inline] fn i64(&mut self, value: i64) { - self.field("i64", &value); + self.entry(&format_args!("i64: {}", value)); } #[inline] fn u128(&mut self, value: u128) { - self.field("u128", &value); + self.entry(&format_args!("u128: {}", value)); } #[inline] fn i128(&mut self, value: i128) { - self.field("i128", &value); + self.entry(&format_args!("i128: {}", value)); } #[inline] fn f32(&mut self, value: f32) { - self.field("f32", &value); + self.entry(&format_args!("f32: {}", value)); } #[inline] fn f64(&mut self, value: f64) { - self.field("f64", &value); + self.entry(&format_args!("f64: {}", value)); } #[inline] fn str(&mut self, value: Cow<'_, str>) { let value: &str = &value; - self.field("str", &value); + self.entry(&format_args!("str: {:?}", value)); } #[inline] fn bytes(&mut self, value: Cow<'_, [u8]>) { match String::from_utf8(value.to_vec()) { - Ok(s) => self.field("bytes", &format_args!("b{s:?}")), - Err(_) => self.field("bytes", &value.as_ref()), + Ok(s) => self.entry(&format_args!("bytes: b{:?}", s)), + Err(_) => self.entry(&format_args!("bytes: {:?}", value)), }; } }