Implement debug_more_non_exhaustive

Add a `.finish_non_exhaustive()` method to `DebugTuple`, `DebugSet`,
`DebugList`, and `DebugMap`. This indicates that the structures have
remaining items with `..`.

This implements the ACP at
<https://github.com/rust-lang/libs-team/issues/248>.
This commit is contained in:
Trevor Gross 2024-07-18 20:33:22 -04:00
parent 68fb25e2eb
commit 827970ebe9
3 changed files with 521 additions and 2 deletions

View File

@ -360,6 +360,51 @@ pub fn field_with<F>(&mut self, value_fmt: F) -> &mut Self
self
}
/// Marks the tuple struct as non-exhaustive, indicating to the reader that there are some
/// other fields that are not shown in the debug representation.
///
/// # Examples
///
/// ```
/// #![feature(debug_more_non_exhaustive)]
///
/// use std::fmt;
///
/// struct Foo(i32, String);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// fmt.debug_tuple("Foo")
/// .field(&self.0)
/// .finish_non_exhaustive() // Show that some other field(s) exist.
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(10, "secret!".to_owned())),
/// "Foo(10, ..)",
/// );
/// ```
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
self.result = self.result.and_then(|_| {
if self.fields > 0 {
if self.is_pretty() {
let mut slot = None;
let mut state = Default::default();
let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
writer.write_str("..\n")?;
self.fmt.write_str(")")
} else {
self.fmt.write_str(", ..)")
}
} else {
self.fmt.write_str("(..)")
}
});
self.result
}
/// Finishes output and returns any error encountered.
///
/// # Examples
@ -554,6 +599,56 @@ pub fn entries<D, I>(&mut self, entries: I) -> &mut Self
self
}
/// Marks the set as non-exhaustive, indicating to the reader that there are some other
/// elements that are not shown in the debug representation.
///
/// # Examples
///
/// ```
/// #![feature(debug_more_non_exhaustive)]
///
/// use std::fmt;
///
/// struct Foo(Vec<i32>);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// // Print at most two elements, abbreviate the rest
/// let mut f = fmt.debug_set();
/// let mut f = f.entries(self.0.iter().take(2));
/// if self.0.len() > 2 {
/// f.finish_non_exhaustive()
/// } else {
/// f.finish()
/// }
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![1, 2, 3, 4])),
/// "{1, 2, ..}",
/// );
/// ```
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
self.inner.result = self.inner.result.and_then(|_| {
if self.inner.has_fields {
if self.inner.is_pretty() {
let mut slot = None;
let mut state = Default::default();
let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state);
writer.write_str("..\n")?;
self.inner.fmt.write_str("}")
} else {
self.inner.fmt.write_str(", ..}")
}
} else {
self.inner.fmt.write_str("..}")
}
});
self.inner.result
}
/// Finishes output and returns any error encountered.
///
/// # Examples
@ -697,6 +792,55 @@ pub fn entries<D, I>(&mut self, entries: I) -> &mut Self
self
}
/// Marks the list as non-exhaustive, indicating to the reader that there are some other
/// elements that are not shown in the debug representation.
///
/// # Examples
///
/// ```
/// #![feature(debug_more_non_exhaustive)]
///
/// use std::fmt;
///
/// struct Foo(Vec<i32>);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// // Print at most two elements, abbreviate the rest
/// let mut f = fmt.debug_list();
/// let mut f = f.entries(self.0.iter().take(2));
/// if self.0.len() > 2 {
/// f.finish_non_exhaustive()
/// } else {
/// f.finish()
/// }
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![1, 2, 3, 4])),
/// "[1, 2, ..]",
/// );
/// ```
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
self.inner.result.and_then(|_| {
if self.inner.has_fields {
if self.inner.is_pretty() {
let mut slot = None;
let mut state = Default::default();
let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state);
writer.write_str("..\n")?;
self.inner.fmt.write_str("]")
} else {
self.inner.fmt.write_str(", ..]")
}
} else {
self.inner.fmt.write_str("..]")
}
})
}
/// Finishes output and returns any error encountered.
///
/// # Examples
@ -973,6 +1117,62 @@ pub fn entries<K, V, I>(&mut self, entries: I) -> &mut Self
self
}
/// Marks the map as non-exhaustive, indicating to the reader that there are some other
/// entries that are not shown in the debug representation.
///
/// # Examples
///
/// ```
/// #![feature(debug_more_non_exhaustive)]
///
/// use std::fmt;
///
/// struct Foo(Vec<(String, i32)>);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// // Print at most two elements, abbreviate the rest
/// let mut f = fmt.debug_map();
/// let mut f = f.entries(self.0.iter().take(2).map(|&(ref k, ref v)| (k, v)));
/// if self.0.len() > 2 {
/// f.finish_non_exhaustive()
/// } else {
/// f.finish()
/// }
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![
/// ("A".to_string(), 10),
/// ("B".to_string(), 11),
/// ("C".to_string(), 12),
/// ])),
/// r#"{"A": 10, "B": 11, ..}"#,
/// );
/// ```
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
self.result = self.result.and_then(|_| {
assert!(!self.has_key, "attempted to finish a map with a partial entry");
if self.has_fields {
if self.is_pretty() {
let mut slot = None;
let mut state = Default::default();
let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
writer.write_str("..\n")?;
self.fmt.write_str("}")
} else {
self.fmt.write_str(", ..}")
}
} else {
self.fmt.write_str("..}")
}
});
self.result
}
/// Finishes output and returns any error encountered.
///
/// # Panics

View File

@ -257,6 +257,80 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
10/20,
),
"world",
)"#,
format!("{Bar:#?}")
);
}
#[test]
fn test_empty_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Foo").finish_non_exhaustive()
}
}
assert_eq!("Foo(..)", format!("{Foo:?}"));
assert_eq!("Foo(..)", format!("{Foo:#?}"));
}
#[test]
fn test_multiple_and_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Foo")
.field(&true)
.field(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
assert_eq!("Foo(true, 10/20, ..)", format!("{Foo:?}"));
assert_eq!(
"Foo(
true,
10/20,
..
)",
format!("{Foo:#?}")
);
}
#[test]
fn test_nested_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Foo")
.field(&true)
.field(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
struct Bar;
impl fmt::Debug for Bar {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Bar").field(&Foo).field(&"world").finish_non_exhaustive()
}
}
assert_eq!(r#"Bar(Foo(true, 10/20, ..), "world", ..)"#, format!("{Bar:?}"));
assert_eq!(
r#"Bar(
Foo(
true,
10/20,
..
),
"world",
..
)"#,
format!("{Bar:#?}")
);
@ -371,8 +445,7 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
}
assert_eq!(
r#"{"foo": {"bar": true, 10: 10/20}, \
{"bar": true, 10: 10/20}: "world"}"#,
r#"{"foo": {"bar": true, 10: 10/20}, {"bar": true, 10: 10/20}: "world"}"#,
format!("{Bar:?}")
);
assert_eq!(
@ -471,6 +544,103 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let _ = format!("{Foo:?}");
}
#[test]
fn test_empty_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map().finish_non_exhaustive()
}
}
assert_eq!("{..}", format!("{Foo:?}"));
assert_eq!("{..}", format!("{Foo:#?}"));
}
#[test]
fn test_multiple_and_non_exhaustive() {
struct Entry;
impl fmt::Debug for Entry {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.entry(&"bar", &true)
.entry(&10, &format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
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_non_exhaustive()
}
}
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}"));
assert_eq!(r#"{"bar": true, 10: 10/20, ..}"#, format!("{Entry:?}"));
assert_eq!(
r#"{
"bar": true,
10: 10/20,
..
}"#,
format!("{Entry:#?}")
);
}
#[test]
fn test_nested_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.entry(&"bar", &true)
.entry(&10, &format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
struct Bar;
impl fmt::Debug for Bar {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map().entry(&"foo", &Foo).entry(&Foo, &"world").finish_non_exhaustive()
}
}
assert_eq!(
r#"{"foo": {"bar": true, 10: 10/20, ..}, {"bar": true, 10: 10/20, ..}: "world", ..}"#,
format!("{Bar:?}")
);
assert_eq!(
r#"{
"foo": {
"bar": true,
10: 10/20,
..
},
{
"bar": true,
10: 10/20,
..
}: "world",
..
}"#,
format!("{Bar:#?}")
);
}
}
mod debug_set {
@ -555,6 +725,80 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
10/20,
},
"world",
}"#,
format!("{Bar:#?}")
);
}
#[test]
fn test_empty_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set().finish_non_exhaustive()
}
}
assert_eq!("{..}", format!("{Foo:?}"));
assert_eq!("{..}", format!("{Foo:#?}"));
}
#[test]
fn test_multiple_and_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set()
.entry(&true)
.entry(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
assert_eq!("{true, 10/20, ..}", format!("{Foo:?}"));
assert_eq!(
"{
true,
10/20,
..
}",
format!("{Foo:#?}")
);
}
#[test]
fn test_nested_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set()
.entry(&true)
.entry(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
struct Bar;
impl fmt::Debug for Bar {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set().entry(&Foo).entry(&"world").finish_non_exhaustive()
}
}
assert_eq!(r#"{{true, 10/20, ..}, "world", ..}"#, format!("{Bar:?}"));
assert_eq!(
r#"{
{
true,
10/20,
..
},
"world",
..
}"#,
format!("{Bar:#?}")
);
@ -643,6 +887,80 @@ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
10/20,
],
"world",
]"#,
format!("{Bar:#?}")
);
}
#[test]
fn test_empty_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_list().finish_non_exhaustive()
}
}
assert_eq!("[..]", format!("{Foo:?}"));
assert_eq!("[..]", format!("{Foo:#?}"));
}
#[test]
fn test_multiple_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_list()
.entry(&true)
.entry(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
assert_eq!("[true, 10/20, ..]", format!("{Foo:?}"));
assert_eq!(
"[
true,
10/20,
..
]",
format!("{Foo:#?}")
);
}
#[test]
fn test_nested_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_list()
.entry(&true)
.entry(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
struct Bar;
impl fmt::Debug for Bar {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_list().entry(&Foo).entry(&"world").finish_non_exhaustive()
}
}
assert_eq!(r#"[[true, 10/20, ..], "world", ..]"#, format!("{Bar:?}"));
assert_eq!(
r#"[
[
true,
10/20,
..
],
"world",
..
]"#,
format!("{Bar:#?}")
);

View File

@ -29,6 +29,7 @@
#![feature(core_io_borrowed_buf)]
#![feature(core_private_bignum)]
#![feature(core_private_diy_float)]
#![feature(debug_more_non_exhaustive)]
#![feature(dec2flt)]
#![feature(duration_consts_float)]
#![feature(duration_constants)]