Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add key and value methods to DebugMap #60458

Merged
merged 2 commits into from
Jul 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
160 changes: 143 additions & 17 deletions src/libcore/fmt/builders.rs
Original file line number Diff line number Diff line change
@@ -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,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shifting the state of the PadAdapter into a dedicated struct here lets value pick up where key left off without having to guess what the state was before.

}

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()
}
};
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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> {
Expand All @@ -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(),
}
}

Expand Down Expand Up @@ -712,25 +733,123 @@ 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
///
/// ```
/// # #![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(|_| {
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
///
/// ```
/// # #![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;
Expand Down Expand Up @@ -775,6 +894,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
///
/// ```
Expand All @@ -797,6 +921,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("}"))
}

Expand Down
Loading