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:
@@ -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.
|
||||||
@@ -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("}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
Reference in New Issue
Block a user