Rollup merge of #60458 - KodrAus:debug_map_entry, r=alexcrichton

Add key and value methods to DebugMap

Implementation PR for an active (not approved) RFC: https://github.com/rust-lang/rfcs/pull/2696.

Add two new methods to `std::fmt::DebugMap` for writing the key and value part of a map entry separately:

```rust
impl<'a, 'b: 'a> DebugMap<'a, 'b> {
    pub fn key(&mut self, key: &dyn Debug) -> &mut Self;
    pub fn value(&mut self, value: &dyn Debug) -> &mut Self;
}
```

I want to do this so that I can write a `serde::Serializer` that forwards to our format builders, so that any `T: Serialize` can also be treated like a `T: Debug`.
This commit is contained in:
Mazdak Farrokhzad
2019-07-09 04:52:35 +02:00
committed by GitHub
4 changed files with 238 additions and 25 deletions

View File

@@ -0,0 +1,9 @@
# `debug_map_key_value`
The tracking issue for this feature is: [#62482]
[#62482]: https://github.com/rust-lang/rust/issues/62482
------------------------
Add the methods `key` and `value` to `DebugMap` so that an entry can be formatted across multiple calls without additional buffering.

View File

@@ -1,37 +1,50 @@
use crate::fmt; use crate::fmt;
struct PadAdapter<'a> { struct PadAdapter<'buf, 'state> {
buf: &'a mut (dyn fmt::Write + 'a), buf: &'buf mut (dyn fmt::Write + 'buf),
state: &'state mut PadAdapterState,
}
struct PadAdapterState {
on_newline: bool, on_newline: bool,
} }
impl<'a> PadAdapter<'a> { impl Default for PadAdapterState {
fn wrap<'b, 'c: 'a+'b>(fmt: &'c mut fmt::Formatter<'_>, slot: &'b mut Option<Self>) fn default() -> Self {
-> fmt::Formatter<'b> { PadAdapterState {
on_newline: true,
}
}
}
impl<'buf, 'state> PadAdapter<'buf, 'state> {
fn wrap<'slot, 'fmt: 'buf+'slot>(fmt: &'fmt mut fmt::Formatter<'_>,
slot: &'slot mut Option<Self>,
state: &'state mut PadAdapterState) -> fmt::Formatter<'slot> {
fmt.wrap_buf(move |buf| { fmt.wrap_buf(move |buf| {
*slot = Some(PadAdapter { *slot = Some(PadAdapter {
buf, buf,
on_newline: true, state,
}); });
slot.as_mut().unwrap() slot.as_mut().unwrap()
}) })
} }
} }
impl fmt::Write for PadAdapter<'_> { impl fmt::Write for PadAdapter<'_, '_> {
fn write_str(&mut self, mut s: &str) -> fmt::Result { fn write_str(&mut self, mut s: &str) -> fmt::Result {
while !s.is_empty() { while !s.is_empty() {
if self.on_newline { if self.state.on_newline {
self.buf.write_str(" ")?; self.buf.write_str(" ")?;
} }
let split = match s.find('\n') { let split = match s.find('\n') {
Some(pos) => { Some(pos) => {
self.on_newline = true; self.state.on_newline = true;
pos + 1 pos + 1
} }
None => { None => {
self.on_newline = false; self.state.on_newline = false;
s.len() s.len()
} }
}; };
@@ -133,7 +146,8 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
self.fmt.write_str(" {\n")?; self.fmt.write_str(" {\n")?;
} }
let mut slot = None; let mut slot = None;
let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot); let mut state = Default::default();
let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state);
writer.write_str(name)?; writer.write_str(name)?;
writer.write_str(": ")?; writer.write_str(": ")?;
value.fmt(&mut writer)?; value.fmt(&mut writer)?;
@@ -279,7 +293,8 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
self.fmt.write_str("(\n")?; self.fmt.write_str("(\n")?;
} }
let mut slot = None; let mut slot = None;
let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot); let mut state = Default::default();
let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state);
value.fmt(&mut writer)?; value.fmt(&mut writer)?;
writer.write_str(",\n") writer.write_str(",\n")
} else { } else {
@@ -349,7 +364,8 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> {
self.fmt.write_str("\n")?; self.fmt.write_str("\n")?;
} }
let mut slot = None; let mut slot = None;
let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot); let mut state = Default::default();
let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut state);
entry.fmt(&mut writer)?; entry.fmt(&mut writer)?;
writer.write_str(",\n") writer.write_str(",\n")
} else { } else {
@@ -676,6 +692,9 @@ pub struct DebugMap<'a, 'b: 'a> {
fmt: &'a mut fmt::Formatter<'b>, fmt: &'a mut fmt::Formatter<'b>,
result: fmt::Result, result: fmt::Result,
has_fields: bool, has_fields: bool,
has_key: bool,
// The state of newlines is tracked between keys and values
state: PadAdapterState,
} }
pub fn debug_map_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>) -> DebugMap<'a, 'b> { pub fn debug_map_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>) -> DebugMap<'a, 'b> {
@@ -684,6 +703,8 @@ pub fn debug_map_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>) -> DebugMap<'a, 'b
fmt, fmt,
result, result,
has_fields: false, has_fields: false,
has_key: false,
state: Default::default(),
} }
} }
@@ -712,25 +733,123 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
/// ``` /// ```
#[stable(feature = "debug_builders", since = "1.2.0")] #[stable(feature = "debug_builders", since = "1.2.0")]
pub fn entry(&mut self, key: &dyn fmt::Debug, value: &dyn fmt::Debug) -> &mut DebugMap<'a, 'b> { pub fn entry(&mut self, key: &dyn fmt::Debug, value: &dyn fmt::Debug) -> &mut DebugMap<'a, 'b> {
self.key(key).value(value)
}
/// Adds the key part of a new entry to the map output.
///
/// This method, together with `value`, is an alternative to `entry` that
/// can be used when the complete entry isn't known upfront. Prefer the `entry`
/// method when it's possible to use.
///
/// # Panics
///
/// `key` must be called before `value` and each call to `key` must be followed
/// by a corresponding call to `value`. Otherwise this method will panic.
///
/// # Examples
///
/// ```
/// # #![feature(debug_map_key_value)]
/// use std::fmt;
///
/// struct Foo(Vec<(String, i32)>);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// fmt.debug_map()
/// .key(&"whole").value(&self.0) // We add the "whole" entry.
/// .finish()
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
/// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}",
/// );
/// ```
#[unstable(feature = "debug_map_key_value",
reason = "recently added",
issue = "62482")]
pub fn key(&mut self, key: &dyn fmt::Debug) -> &mut DebugMap<'a, 'b> {
assert!(!self.has_key, "attempted to begin a new map entry \
without completing the previous one");
self.result = self.result.and_then(|_| { self.result = self.result.and_then(|_| {
if self.is_pretty() { if self.is_pretty() {
if !self.has_fields { if !self.has_fields {
self.fmt.write_str("\n")?; self.fmt.write_str("\n")?;
} }
let mut slot = None; let mut slot = None;
let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot); self.state = Default::default();
let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut self.state);
key.fmt(&mut writer)?; key.fmt(&mut writer)?;
writer.write_str(": ")?; writer.write_str(": ")?;
value.fmt(&mut writer)?;
writer.write_str(",\n")
} else { } else {
if self.has_fields { if self.has_fields {
self.fmt.write_str(", ")? self.fmt.write_str(", ")?
} }
key.fmt(self.fmt)?; key.fmt(self.fmt)?;
self.fmt.write_str(": ")?; self.fmt.write_str(": ")?;
value.fmt(self.fmt)
} }
self.has_key = true;
Ok(())
});
self
}
/// Adds the value part of a new entry to the map output.
///
/// This method, together with `key`, is an alternative to `entry` that
/// can be used when the complete entry isn't known upfront. Prefer the `entry`
/// method when it's possible to use.
///
/// # Panics
///
/// `key` must be called before `value` and each call to `key` must be followed
/// by a corresponding call to `value`. Otherwise this method will panic.
///
/// # Examples
///
/// ```
/// # #![feature(debug_map_key_value)]
/// use std::fmt;
///
/// struct Foo(Vec<(String, i32)>);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// fmt.debug_map()
/// .key(&"whole").value(&self.0) // We add the "whole" entry.
/// .finish()
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
/// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}",
/// );
/// ```
#[unstable(feature = "debug_map_key_value",
reason = "recently added",
issue = "62482")]
pub fn value(&mut self, value: &dyn fmt::Debug) -> &mut DebugMap<'a, 'b> {
assert!(self.has_key, "attempted to format a map value before its key");
self.result = self.result.and_then(|_| {
if self.is_pretty() {
let mut slot = None;
let mut writer = PadAdapter::wrap(&mut self.fmt, &mut slot, &mut self.state);
value.fmt(&mut writer)?;
writer.write_str(",\n")?;
} else {
value.fmt(self.fmt)?;
}
self.has_key = false;
Ok(())
}); });
self.has_fields = true; self.has_fields = true;
@@ -775,6 +894,11 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
/// Finishes output and returns any error encountered. /// Finishes output and returns any error encountered.
/// ///
/// # Panics
///
/// `key` must be called before `value` and each call to `key` must be followed
/// by a corresponding call to `value`. Otherwise this method will panic.
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
@@ -797,6 +921,8 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
/// ``` /// ```
#[stable(feature = "debug_builders", since = "1.2.0")] #[stable(feature = "debug_builders", since = "1.2.0")]
pub fn finish(&mut self) -> fmt::Result { pub fn finish(&mut self) -> fmt::Result {
assert!(!self.has_key, "attempted to finish a map with a partial entry");
self.result.and_then(|_| self.fmt.write_str("}")) self.result.and_then(|_| self.fmt.write_str("}"))
} }

View File

@@ -211,9 +211,9 @@ mod debug_map {
#[test] #[test]
fn test_single() { fn test_single() {
struct Foo; struct Entry;
impl fmt::Debug for Foo { impl fmt::Debug for Entry {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map() fmt.debug_map()
.entry(&"bar", &true) .entry(&"bar", &true)
@@ -221,19 +221,32 @@ mod debug_map {
} }
} }
assert_eq!("{\"bar\": true}", format!("{:?}", Foo)); struct KeyValue;
impl fmt::Debug for KeyValue {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.key(&"bar").value(&true)
.finish()
}
}
assert_eq!(format!("{:?}", Entry), format!("{:?}", KeyValue));
assert_eq!(format!("{:#?}", Entry), format!("{:#?}", KeyValue));
assert_eq!("{\"bar\": true}", format!("{:?}", Entry));
assert_eq!( assert_eq!(
"{ "{
\"bar\": true, \"bar\": true,
}", }",
format!("{:#?}", Foo)); format!("{:#?}", Entry));
} }
#[test] #[test]
fn test_multiple() { fn test_multiple() {
struct Foo; struct Entry;
impl fmt::Debug for Foo { impl fmt::Debug for Entry {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map() fmt.debug_map()
.entry(&"bar", &true) .entry(&"bar", &true)
@@ -242,13 +255,27 @@ mod debug_map {
} }
} }
assert_eq!("{\"bar\": true, 10: 10/20}", format!("{:?}", Foo)); struct KeyValue;
impl fmt::Debug for KeyValue {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.key(&"bar").value(&true)
.key(&10).value(&format_args!("{}/{}", 10, 20))
.finish()
}
}
assert_eq!(format!("{:?}", Entry), format!("{:?}", KeyValue));
assert_eq!(format!("{:#?}", Entry), format!("{:#?}", KeyValue));
assert_eq!("{\"bar\": true, 10: 10/20}", format!("{:?}", Entry));
assert_eq!( assert_eq!(
"{ "{
\"bar\": true, \"bar\": true,
10: 10/20, 10: 10/20,
}", }",
format!("{:#?}", Foo)); format!("{:#?}", Entry));
} }
#[test] #[test]
@@ -291,6 +318,56 @@ mod debug_map {
}", }",
format!("{:#?}", Bar)); format!("{:#?}", Bar));
} }
#[test]
#[should_panic]
fn test_invalid_key_when_entry_is_incomplete() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.key(&"bar")
.key(&"invalid")
.finish()
}
}
format!("{:?}", Foo);
}
#[test]
#[should_panic]
fn test_invalid_finish_incomplete_entry() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.key(&"bar")
.finish()
}
}
format!("{:?}", Foo);
}
#[test]
#[should_panic]
fn test_invalid_value_before_key() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.value(&"invalid")
.key(&"bar")
.finish()
}
}
format!("{:?}", Foo);
}
} }
mod debug_set { mod debug_set {

View File

@@ -3,6 +3,7 @@
#![feature(cell_update)] #![feature(cell_update)]
#![feature(core_private_bignum)] #![feature(core_private_bignum)]
#![feature(core_private_diy_float)] #![feature(core_private_diy_float)]
#![feature(debug_map_key_value)]
#![feature(dec2flt)] #![feature(dec2flt)]
#![feature(euclidean_division)] #![feature(euclidean_division)]
#![feature(exact_size_is_empty)] #![feature(exact_size_is_empty)]