Source code
Revision control
Copy as Markdown
Other Tools
//! [`core::fmt::DebugTuple`] reimplementation with
//! [`DebugTuple::finish_non_exhaustive()`] method.
use ::core;
use core::fmt::{Debug, Formatter, Result, Write};
use core::prelude::v1::*;
/// Same as [`core::fmt::DebugTuple`], but with
/// [`DebugTuple::finish_non_exhaustive()`] method.
#[must_use = "must eventually call `finish()` or `finish_non_exhaustive()` on \
Debug builders"]
pub struct DebugTuple<'a, 'b: 'a> {
fmt: &'a mut Formatter<'b>,
result: Result,
fields: usize,
empty_name: bool,
}
/// Creates a new [`DebugTuple`].
pub fn debug_tuple<'a, 'b>(
fmt: &'a mut Formatter<'b>,
name: &str,
) -> DebugTuple<'a, 'b> {
let result = fmt.write_str(name);
DebugTuple {
fmt,
result,
fields: 0,
empty_name: name.is_empty(),
}
}
impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
/// Adds a new field to the generated tuple struct output.
///
/// # Example
///
/// ```rust
/// use core::fmt;
/// use derive_more::__private::debug_tuple;
///
/// struct Foo(i32, String);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// debug_tuple(fmt, "Foo")
/// .field(&self.0) // We add the first field.
/// .field(&self.1) // We add the second field.
/// .finish() // We're good to go!
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(10, "Hello World".to_string())),
/// "Foo(10, \"Hello World\")",
/// );
/// ```
pub fn field(&mut self, value: &dyn Debug) -> &mut Self {
self.result = self.result.and_then(|_| {
if self.is_pretty() {
if self.fields == 0 {
self.fmt.write_str("(\n")?;
}
let mut padded_formatter = Padded::new(self.fmt);
padded_formatter.write_fmt(format_args!("{value:#?}"))?;
padded_formatter.write_str(",\n")
} else {
let prefix = if self.fields == 0 { "(" } else { ", " };
self.fmt.write_str(prefix)?;
value.fmt(self.fmt)
}
});
self.fields += 1;
self
}
/// Finishes output and returns any error encountered.
///
/// # Example
///
/// ```
/// use core::fmt;
/// use derive_more::__private::debug_tuple;
///
/// struct Foo(i32, String);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// debug_tuple(fmt, "Foo")
/// .field(&self.0)
/// .field(&self.1)
/// .finish() // You need to call it to "finish" the
/// // tuple formatting.
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(10, "Hello World".to_string())),
/// "Foo(10, \"Hello World\")",
/// );
/// ```
pub fn finish(&mut self) -> Result {
if self.fields > 0 {
self.result = self.result.and_then(|_| {
if self.fields == 1 && self.empty_name && !self.is_pretty() {
self.fmt.write_str(",")?;
}
self.fmt.write_str(")")
});
}
self.result
}
/// Marks the struct as non-exhaustive, indicating to the reader that there are some other
/// fields that are not shown in the debug representation, and finishes output, returning any
/// error encountered.
///
/// # Example
///
/// ```rust
/// use core::fmt;
/// use derive_more::__private::debug_tuple;
///
/// struct Bar(i32, f32);
///
/// impl fmt::Debug for Bar {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// debug_tuple(fmt, "Bar")
/// .field(&self.0)
/// .finish_non_exhaustive() // Show that some other field(s) exist.
/// }
/// }
///
/// assert_eq!(format!("{:?}", Bar(10, 1.0)), "Bar(10, ..)");
/// ```
pub fn finish_non_exhaustive(&mut self) -> Result {
self.result = self.result.and_then(|_| {
if self.fields > 0 {
if self.is_pretty() {
let mut padded_formatter = Padded::new(self.fmt);
padded_formatter.write_str("..\n")?;
self.fmt.write_str(")")
} else {
self.fmt.write_str(", ..)")
}
} else {
self.fmt.write_str("(..)")
}
});
self.result
}
fn is_pretty(&self) -> bool {
self.fmt.alternate()
}
}
/// Wrapper for a [`Formatter`] adding 4 spaces on newlines for inner pretty
/// printed [`Debug`] values.
struct Padded<'a, 'b> {
formatter: &'a mut Formatter<'b>,
on_newline: bool,
}
impl<'a, 'b> Padded<'a, 'b> {
fn new(formatter: &'a mut Formatter<'b>) -> Self {
Self {
formatter,
on_newline: true,
}
}
}
impl<'a, 'b> Write for Padded<'a, 'b> {
fn write_str(&mut self, s: &str) -> Result {
for s in s.split_inclusive('\n') {
if self.on_newline {
self.formatter.write_str(" ")?;
}
self.on_newline = s.ends_with('\n');
self.formatter.write_str(s)?;
}
Ok(())
}
}