mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-04 20:54:13 +00:00
add key and value methods to DebugMap
This commit is contained in:
parent
d3e2cec292
commit
e98ea52762
@ -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;
|
||||
|
||||
struct PadAdapter<'a> {
|
||||
buf: &'a mut (dyn fmt::Write + 'a),
|
||||
struct PadAdapter<'buf, 'state> {
|
||||
buf: &'buf mut (dyn fmt::Write + 'buf),
|
||||
state: &'state mut PadAdapterState,
|
||||
}
|
||||
|
||||
struct PadAdapterState {
|
||||
on_newline: bool,
|
||||
}
|
||||
|
||||
impl<'a> PadAdapter<'a> {
|
||||
fn wrap<'b, 'c: 'a+'b>(fmt: &'c mut fmt::Formatter<'_>, slot: &'b mut Option<Self>)
|
||||
-> fmt::Formatter<'b> {
|
||||
impl Default for PadAdapterState {
|
||||
fn default() -> Self {
|
||||
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| {
|
||||
*slot = Some(PadAdapter {
|
||||
buf,
|
||||
on_newline: true,
|
||||
state,
|
||||
});
|
||||
slot.as_mut().unwrap()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for PadAdapter<'_> {
|
||||
impl fmt::Write for PadAdapter<'_, '_> {
|
||||
fn write_str(&mut self, mut s: &str) -> fmt::Result {
|
||||
while !s.is_empty() {
|
||||
if self.on_newline {
|
||||
if self.state.on_newline {
|
||||
self.buf.write_str(" ")?;
|
||||
}
|
||||
|
||||
let split = match s.find('\n') {
|
||||
Some(pos) => {
|
||||
self.on_newline = true;
|
||||
self.state.on_newline = true;
|
||||
pos + 1
|
||||
}
|
||||
None => {
|
||||
self.on_newline = false;
|
||||
self.state.on_newline = false;
|
||||
s.len()
|
||||
}
|
||||
};
|
||||
@ -133,7 +146,8 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
|
||||
self.fmt.write_str(" {\n")?;
|
||||
}
|
||||
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(": ")?;
|
||||
value.fmt(&mut writer)?;
|
||||
@ -279,7 +293,8 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
|
||||
self.fmt.write_str("(\n")?;
|
||||
}
|
||||
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)?;
|
||||
writer.write_str(",\n")
|
||||
} else {
|
||||
@ -349,7 +364,8 @@ impl<'a, 'b: 'a> DebugInner<'a, 'b> {
|
||||
self.fmt.write_str("\n")?;
|
||||
}
|
||||
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)?;
|
||||
writer.write_str(",\n")
|
||||
} else {
|
||||
@ -676,6 +692,9 @@ pub struct DebugMap<'a, 'b: 'a> {
|
||||
fmt: &'a mut fmt::Formatter<'b>,
|
||||
result: fmt::Result,
|
||||
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> {
|
||||
@ -684,6 +703,8 @@ pub fn debug_map_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>) -> DebugMap<'a, 'b
|
||||
fmt,
|
||||
result,
|
||||
has_fields: false,
|
||||
has_key: false,
|
||||
state: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -712,25 +733,121 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||
/// ```
|
||||
#[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> {
|
||||
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
|
||||
///
|
||||
/// ```
|
||||
/// 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(|_| {
|
||||
if self.is_pretty() {
|
||||
if !self.has_fields {
|
||||
self.fmt.write_str("\n")?;
|
||||
}
|
||||
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)?;
|
||||
writer.write_str(": ")?;
|
||||
value.fmt(&mut writer)?;
|
||||
writer.write_str(",\n")
|
||||
} else {
|
||||
if self.has_fields {
|
||||
self.fmt.write_str(", ")?
|
||||
}
|
||||
key.fmt(self.fmt)?;
|
||||
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
|
||||
///
|
||||
/// ```
|
||||
/// 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;
|
||||
@ -775,6 +892,11 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
@ -797,6 +919,8 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
|
||||
/// ```
|
||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||
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("}"))
|
||||
}
|
||||
|
||||
|
@ -211,9 +211,9 @@ mod debug_map {
|
||||
|
||||
#[test]
|
||||
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 {
|
||||
fmt.debug_map()
|
||||
.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!(
|
||||
"{
|
||||
\"bar\": true,
|
||||
}",
|
||||
format!("{:#?}", Foo));
|
||||
format!("{:#?}", Entry));
|
||||
}
|
||||
|
||||
#[test]
|
||||
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 {
|
||||
fmt.debug_map()
|
||||
.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!(
|
||||
"{
|
||||
\"bar\": true,
|
||||
10: 10/20,
|
||||
}",
|
||||
format!("{:#?}", Foo));
|
||||
format!("{:#?}", Entry));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -291,6 +318,56 @@ mod debug_map {
|
||||
}",
|
||||
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 {
|
||||
|
@ -3,6 +3,7 @@
|
||||
#![feature(cell_update)]
|
||||
#![feature(core_private_bignum)]
|
||||
#![feature(core_private_diy_float)]
|
||||
#![feature(debug_map_key_value)]
|
||||
#![feature(dec2flt)]
|
||||
#![feature(euclidean_division)]
|
||||
#![feature(exact_size_is_empty)]
|
||||
|
Loading…
Reference in New Issue
Block a user