about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--library/alloc/src/fmt.rs2
-rw-r--r--library/core/src/fmt/builders.rs144
-rw-r--r--library/core/src/fmt/mod.rs3
3 files changed, 136 insertions, 13 deletions
diff --git a/library/alloc/src/fmt.rs b/library/alloc/src/fmt.rs
index 1e2c35bf735..5b50ef7bf6c 100644
--- a/library/alloc/src/fmt.rs
+++ b/library/alloc/src/fmt.rs
@@ -555,6 +555,8 @@
 pub use core::fmt::Alignment;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::fmt::Error;
+#[unstable(feature = "debug_closure_helpers", issue = "117729")]
+pub use core::fmt::FormatterFn;
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use core::fmt::{write, Arguments};
 #[stable(feature = "rust1", since = "1.0.0")]
diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs
index 47db53ac6f3..4ccb585862c 100644
--- a/library/core/src/fmt/builders.rs
+++ b/library/core/src/fmt/builders.rs
@@ -130,6 +130,18 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
     /// ```
     #[stable(feature = "debug_builders", since = "1.2.0")]
     pub fn field(&mut self, name: &str, value: &dyn fmt::Debug) -> &mut Self {
+        self.field_with(name, |f| value.fmt(f))
+    }
+
+    /// Adds a new field to the generated struct output.
+    ///
+    /// This method is equivalent to [`DebugStruct::field`], but formats the
+    /// value using a provided closure rather than by calling [`Debug::fmt`].
+    #[unstable(feature = "debug_closure_helpers", issue = "117729")]
+    pub fn field_with<F>(&mut self, name: &str, value_fmt: F) -> &mut Self
+    where
+        F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
+    {
         self.result = self.result.and_then(|_| {
             if self.is_pretty() {
                 if !self.has_fields {
@@ -140,14 +152,14 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
                 let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
                 writer.write_str(name)?;
                 writer.write_str(": ")?;
-                value.fmt(&mut writer)?;
+                value_fmt(&mut writer)?;
                 writer.write_str(",\n")
             } else {
                 let prefix = if self.has_fields { ", " } else { " { " };
                 self.fmt.write_str(prefix)?;
                 self.fmt.write_str(name)?;
                 self.fmt.write_str(": ")?;
-                value.fmt(self.fmt)
+                value_fmt(self.fmt)
             }
         });
 
@@ -315,6 +327,18 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
     /// ```
     #[stable(feature = "debug_builders", since = "1.2.0")]
     pub fn field(&mut self, value: &dyn fmt::Debug) -> &mut Self {
+        self.field_with(|f| value.fmt(f))
+    }
+
+    /// Adds a new field to the generated tuple struct output.
+    ///
+    /// This method is equivalent to [`DebugTuple::field`], but formats the
+    /// value using a provided closure rather than by calling [`Debug::fmt`].
+    #[unstable(feature = "debug_closure_helpers", issue = "117729")]
+    pub fn field_with<F>(&mut self, value_fmt: F) -> &mut Self
+    where
+        F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
+    {
         self.result = self.result.and_then(|_| {
             if self.is_pretty() {
                 if self.fields == 0 {
@@ -323,12 +347,12 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
                 let mut slot = None;
                 let mut state = Default::default();
                 let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
-                value.fmt(&mut writer)?;
+                value_fmt(&mut writer)?;
                 writer.write_str(",\n")
             } else {
                 let prefix = if self.fields == 0 { "(" } else { ", " };
                 self.fmt.write_str(prefix)?;
-                value.fmt(self.fmt)
+                value_fmt(self.fmt)
             }
         });
 
@@ -385,7 +409,10 @@ struct DebugInner<'a, 'b: 'a> {
 }
 
 impl<'a, 'b: 'a> DebugInner<'a, 'b> {
-    fn entry(&mut self, entry: &dyn fmt::Debug) {
+    fn entry_with<F>(&mut self, entry_fmt: F)
+    where
+        F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
+    {
         self.result = self.result.and_then(|_| {
             if self.is_pretty() {
                 if !self.has_fields {
@@ -394,13 +421,13 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> {
                 let mut slot = None;
                 let mut state = Default::default();
                 let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
-                entry.fmt(&mut writer)?;
+                entry_fmt(&mut writer)?;
                 writer.write_str(",\n")
             } else {
                 if self.has_fields {
                     self.fmt.write_str(", ")?
                 }
-                entry.fmt(self.fmt)
+                entry_fmt(self.fmt)
             }
         });
 
@@ -475,7 +502,20 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> {
     /// ```
     #[stable(feature = "debug_builders", since = "1.2.0")]
     pub fn entry(&mut self, entry: &dyn fmt::Debug) -> &mut Self {
-        self.inner.entry(entry);
+        self.inner.entry_with(|f| entry.fmt(f));
+        self
+    }
+
+    /// Adds a new entry to the set output.
+    ///
+    /// This method is equivalent to [`DebugSet::entry`], but formats the
+    /// entry using a provided closure rather than by calling [`Debug::fmt`].
+    #[unstable(feature = "debug_closure_helpers", issue = "117729")]
+    pub fn entry_with<F>(&mut self, entry_fmt: F) -> &mut Self
+    where
+        F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
+    {
+        self.inner.entry_with(entry_fmt);
         self
     }
 
@@ -605,7 +645,20 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
     /// ```
     #[stable(feature = "debug_builders", since = "1.2.0")]
     pub fn entry(&mut self, entry: &dyn fmt::Debug) -> &mut Self {
-        self.inner.entry(entry);
+        self.inner.entry_with(|f| entry.fmt(f));
+        self
+    }
+
+    /// Adds a new entry to the list output.
+    ///
+    /// This method is equivalent to [`DebugList::entry`], but formats the
+    /// entry using a provided closure rather than by calling [`Debug::fmt`].
+    #[unstable(feature = "debug_closure_helpers", issue = "117729")]
+    pub fn entry_with<F>(&mut self, entry_fmt: F) -> &mut Self
+    where
+        F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
+    {
+        self.inner.entry_with(entry_fmt);
         self
     }
 
@@ -775,6 +828,18 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
     /// ```
     #[stable(feature = "debug_map_key_value", since = "1.42.0")]
     pub fn key(&mut self, key: &dyn fmt::Debug) -> &mut Self {
+        self.key_with(|f| key.fmt(f))
+    }
+
+    /// Adds the key part of a new entry to the map output.
+    ///
+    /// This method is equivalent to [`DebugMap::key`], but formats the
+    /// key using a provided closure rather than by calling [`Debug::fmt`].
+    #[unstable(feature = "debug_closure_helpers", issue = "117729")]
+    pub fn key_with<F>(&mut self, key_fmt: F) -> &mut Self
+    where
+        F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
+    {
         self.result = self.result.and_then(|_| {
             assert!(
                 !self.has_key,
@@ -789,13 +854,13 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
                 let mut slot = None;
                 self.state = Default::default();
                 let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut self.state);
-                key.fmt(&mut writer)?;
+                key_fmt(&mut writer)?;
                 writer.write_str(": ")?;
             } else {
                 if self.has_fields {
                     self.fmt.write_str(", ")?
                 }
-                key.fmt(self.fmt)?;
+                key_fmt(self.fmt)?;
                 self.fmt.write_str(": ")?;
             }
 
@@ -839,16 +904,28 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
     /// ```
     #[stable(feature = "debug_map_key_value", since = "1.42.0")]
     pub fn value(&mut self, value: &dyn fmt::Debug) -> &mut Self {
+        self.value_with(|f| value.fmt(f))
+    }
+
+    /// Adds the value part of a new entry to the map output.
+    ///
+    /// This method is equivalent to [`DebugMap::value`], but formats the
+    /// value using a provided closure rather than by calling [`Debug::fmt`].
+    #[unstable(feature = "debug_closure_helpers", issue = "117729")]
+    pub fn value_with<F>(&mut self, value_fmt: F) -> &mut Self
+    where
+        F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result,
+    {
         self.result = self.result.and_then(|_| {
             assert!(self.has_key, "attempted to format a map value before its key");
 
             if self.is_pretty() {
                 let mut slot = None;
                 let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut self.state);
-                value.fmt(&mut writer)?;
+                value_fmt(&mut writer)?;
                 writer.write_str(",\n")?;
             } else {
-                value.fmt(self.fmt)?;
+                value_fmt(self.fmt)?;
             }
 
             self.has_key = false;
@@ -936,3 +1013,44 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
         self.fmt.alternate()
     }
 }
+
+/// Implements [`fmt::Debug`] and [`fmt::Display`] using a function.
+///
+/// # Examples
+///
+/// ```
+/// #![feature(debug_closure_helpers)]
+/// use std::fmt;
+///
+/// let value = 'a';
+/// assert_eq!(format!("{}", value), "a");
+/// assert_eq!(format!("{:?}", value), "'a'");
+///
+/// let wrapped = fmt::FormatterFn(|f| write!(f, "{:?}", &value));
+/// assert_eq!(format!("{}", wrapped), "'a'");
+/// assert_eq!(format!("{:?}", wrapped), "'a'");
+/// ```
+#[unstable(feature = "debug_closure_helpers", issue = "117729")]
+pub struct FormatterFn<F>(pub F)
+where
+    F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result;
+
+#[unstable(feature = "debug_closure_helpers", issue = "117729")]
+impl<F> fmt::Debug for FormatterFn<F>
+where
+    F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (self.0)(f)
+    }
+}
+
+#[unstable(feature = "debug_closure_helpers", issue = "117729")]
+impl<F> fmt::Display for FormatterFn<F>
+where
+    F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        (self.0)(f)
+    }
+}
diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs
index c45ab094a20..e1b7b46a1ed 100644
--- a/library/core/src/fmt/mod.rs
+++ b/library/core/src/fmt/mod.rs
@@ -39,6 +39,9 @@ pub enum Alignment {
 #[stable(feature = "debug_builders", since = "1.2.0")]
 pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
 
+#[unstable(feature = "debug_closure_helpers", issue = "117729")]
+pub use self::builders::FormatterFn;
+
 /// The type returned by formatter methods.
 ///
 /// # Examples