From b16969d41ca4054d17a79f82c34e4e8344002057 Mon Sep 17 00:00:00 2001 From: Sebastian Speitel Date: Thu, 15 Feb 2024 17:41:54 +0100 Subject: [PATCH] refactor(format): more compact and efficient formatting Use debug set instead of nested debug struct for links. Rename DataFormatter to Format. And trait Method to get verbosity. Split more trait methods. Simplify streaming and serial formatting. --- src/data/ext.rs | 2 +- src/data/format.rs | 382 +++++++++++++++++---------------------------- 2 files changed, 141 insertions(+), 243 deletions(-) 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)), }; } }