diff --git a/src/libcore/fmt/builders.rs b/src/libcore/fmt/builders.rs new file mode 100644 index 000000000000..e7b4cd1122cd --- /dev/null +++ b/src/libcore/fmt/builders.rs @@ -0,0 +1,292 @@ +use prelude::*; +use fmt::{self, Write, FlagV1}; + +struct PadAdapter<'a, 'b: 'a> { + fmt: &'a mut fmt::Formatter<'b>, + on_newline: bool, +} + +impl<'a, 'b: 'a> PadAdapter<'a, 'b> { + fn new(fmt: &'a mut fmt::Formatter<'b>) -> PadAdapter<'a, 'b> { + PadAdapter { + fmt: fmt, + on_newline: false, + } + } +} + +impl<'a, 'b: 'a> fmt::Write for PadAdapter<'a, 'b> { + fn write_str(&mut self, mut s: &str) -> fmt::Result { + while !s.is_empty() { + if self.on_newline { + try!(self.fmt.write_str(" ")); + } + + let split = match s.find('\n') { + Some(pos) => { + self.on_newline = true; + pos + 1 + } + None => { + self.on_newline = false; + s.len() + } + }; + try!(self.fmt.write_str(&s[..split])); + s = &s[split..]; + } + + Ok(()) + } +} + +/// A struct to help with `fmt::Debug` implementations. +/// +/// Constructed by the `Formatter::debug_struct` method. +#[must_use] +pub struct DebugStruct<'a, 'b: 'a> { + fmt: &'a mut fmt::Formatter<'b>, + result: fmt::Result, + has_fields: bool, +} + +pub fn debug_struct_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>, name: &str) + -> DebugStruct<'a, 'b> { + let result = fmt.write_str(name); + DebugStruct { + fmt: fmt, + result: result, + has_fields: false, + } +} + +impl<'a, 'b: 'a> DebugStruct<'a, 'b> { + /// Adds a new field to the generated struct output. + #[unstable(feature = "core", reason = "method was just created")] + pub fn field(mut self, name: &str, value: &S) -> DebugStruct<'a, 'b> + where S: fmt::Debug { + self.result = self.result.and_then(|_| { + let prefix = if self.has_fields { + "," + } else { + " {" + }; + + if self.is_pretty() { + let mut writer = PadAdapter::new(self.fmt); + fmt::write(&mut writer, format_args!("{}\n{}: {:#?}", prefix, name, value)) + } else { + write!(self.fmt, "{} {}: {:?}", prefix, name, value) + } + }); + + self.has_fields = true; + self + } + + /// Consumes the `DebugStruct`, finishing output and returning any error + /// encountered. + #[unstable(feature = "core", reason = "method was just created")] + pub fn finish(mut self) -> fmt::Result { + if self.has_fields { + self.result = self.result.and_then(|_| { + if self.is_pretty() { + self.fmt.write_str("\n}") + } else { + self.fmt.write_str(" }") + } + }); + } + self.result + } + + fn is_pretty(&self) -> bool { + self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + } +} + +/// A struct to help with `fmt::Debug` implementations. +/// +/// Constructed by the `Formatter::debug_tuple` method. +#[must_use] +pub struct DebugTuple<'a, 'b: 'a> { + fmt: &'a mut fmt::Formatter<'b>, + result: fmt::Result, + has_fields: bool, +} + +pub fn debug_tuple_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>, name: &str) -> DebugTuple<'a, 'b> { + let result = fmt.write_str(name); + DebugTuple { + fmt: fmt, + result: result, + has_fields: false, + } +} + +impl<'a, 'b: 'a> DebugTuple<'a, 'b> { + /// Adds a new field to the generated tuple struct output. + #[unstable(feature = "core", reason = "method was just created")] + pub fn field(mut self, value: &S) -> DebugTuple<'a, 'b> where S: fmt::Debug { + self.result = self.result.and_then(|_| { + let (prefix, space) = if self.has_fields { + (",", " ") + } else { + ("(", "") + }; + + if self.is_pretty() { + let mut writer = PadAdapter::new(self.fmt); + fmt::write(&mut writer, format_args!("{}\n{:#?}", prefix, value)) + } else { + write!(self.fmt, "{}{}{:?}", prefix, space, value) + } + }); + + self.has_fields = true; + self + } + + /// Consumes the `DebugTuple`, finishing output and returning any error + /// encountered. + #[unstable(feature = "core", reason = "method was just created")] + pub fn finish(mut self) -> fmt::Result { + if self.has_fields { + self.result = self.result.and_then(|_| { + if self.is_pretty() { + self.fmt.write_str("\n)") + } else { + self.fmt.write_str(")") + } + }); + } + self.result + } + + fn is_pretty(&self) -> bool { + self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + } +} + +/// A struct to help with `fmt::Debug` implementations. +/// +/// Constructed by the `Formatter::debug_set` method. +#[must_use] +pub struct DebugSet<'a, 'b: 'a> { + fmt: &'a mut fmt::Formatter<'b>, + result: fmt::Result, + has_fields: bool, +} + +pub fn debug_set_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>, name: &str) -> DebugSet<'a, 'b> { + let result = write!(fmt, "{} {{", name); + DebugSet { + fmt: fmt, + result: result, + has_fields: false, + } +} + +impl<'a, 'b: 'a> DebugSet<'a, 'b> { + /// Adds a new entry to the set output. + #[unstable(feature = "core", reason = "method was just created")] + pub fn entry(mut self, entry: &S) -> DebugSet<'a, 'b> where S: fmt::Debug { + self.result = self.result.and_then(|_| { + let prefix = if self.has_fields { + "," + } else { + "" + }; + + if self.is_pretty() { + let mut writer = PadAdapter::new(self.fmt); + fmt::write(&mut writer, format_args!("{}\n{:#?}", prefix, entry)) + } else { + write!(self.fmt, "{} {:?}", prefix, entry) + } + }); + + self.has_fields = true; + self + } + + /// Consumes the `DebugSet`, finishing output and returning any error + /// encountered. + #[unstable(feature = "core", reason = "method was just created")] + pub fn finish(self) -> fmt::Result { + self.result.and_then(|_| { + let end = match (self.has_fields, self.is_pretty()) { + (false, _) => "}", + (true, false) => " }", + (true, true) => "\n}", + }; + self.fmt.write_str(end) + }) + } + + fn is_pretty(&self) -> bool { + self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + } +} + +/// A struct to help with `fmt::Debug` implementations. +/// +/// Constructed by the `Formatter::debug_map` method. +#[must_use] +pub struct DebugMap<'a, 'b: 'a> { + fmt: &'a mut fmt::Formatter<'b>, + result: fmt::Result, + has_fields: bool, +} + +pub fn debug_map_new<'a, 'b>(fmt: &'a mut fmt::Formatter<'b>, name: &str) -> DebugMap<'a, 'b> { + let result = write!(fmt, "{} {{", name); + DebugMap { + fmt: fmt, + result: result, + has_fields: false, + } +} + +impl<'a, 'b: 'a> DebugMap<'a, 'b> { + /// Adds a new entry to the map output. + #[unstable(feature = "core", reason = "method was just created")] + pub fn entry(mut self, key: &K, value: &V) -> DebugMap<'a, 'b> + where K: fmt::Debug, V: fmt::Debug { + self.result = self.result.and_then(|_| { + let prefix = if self.has_fields { + "," + } else { + "" + }; + + if self.is_pretty() { + let mut writer = PadAdapter::new(self.fmt); + fmt::write(&mut writer, format_args!("{}\n{:#?}: {:#?}", prefix, key, value)) + } else { + write!(self.fmt, "{} {:?}: {:?}", prefix, key, value) + } + }); + + self.has_fields = true; + self + } + + /// Consumes the `DebugMap`, finishing output and returning any error + /// encountered. + #[unstable(feature = "core", reason = "method was just created")] + pub fn finish(self) -> fmt::Result { + self.result.and_then(|_| { + let end = match (self.has_fields, self.is_pretty()) { + (false, _) => "}", + (true, false) => " }", + (true, true) => "\n}", + }; + self.fmt.write_str(end) + }) + } + + fn is_pretty(&self) -> bool { + self.fmt.flags() & (1 << (FlagV1::Alternate as usize)) != 0 + } +} diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index e640bf02f5a3..572d613f192a 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -32,8 +32,11 @@ pub use self::num::radix; pub use self::num::Radix; pub use self::num::RadixFmt; +pub use self::builders::{DebugStruct, DebugTuple, DebugSet, DebugMap}; + mod num; mod float; +mod builders; #[stable(feature = "rust1", since = "1.0.0")] #[doc(hidden)] @@ -425,7 +428,7 @@ impl<'a> Formatter<'a> { /// # Arguments /// /// * is_positive - whether the original integer was positive or not. - /// * prefix - if the '#' character (FlagAlternate) is provided, this + /// * prefix - if the '#' character (Alternate) is provided, this /// is the prefix to put in front of the number. /// * buf - the byte array that the number has been formatted into /// @@ -614,6 +617,119 @@ impl<'a> Formatter<'a> { /// Optionally specified precision for numeric types #[unstable(feature = "core", reason = "method was just created")] pub fn precision(&self) -> Option { self.precision } + + /// Creates a `DebugStruct` builder designed to assist with creation of + /// `fmt::Debug` implementations for structs. + /// + /// # Examples + /// + /// ```rust + /// use std::fmt; + /// + /// struct Foo { + /// bar: i32, + /// baz: String, + /// } + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// fmt.debug_struct("Foo") + /// .field("bar", &self.bar) + /// .field("baz", &self.baz) + /// .finish() + /// } + /// } + /// + /// // prints "Foo { bar: 10, baz: "Hello World" }" + /// println!("{:?}", Foo { bar: 10, baz: "Hello World".to_string() }); + /// ``` + #[unstable(feature = "core", reason = "method was just created")] + pub fn debug_struct<'b>(&'b mut self, name: &str) -> DebugStruct<'b, 'a> { + builders::debug_struct_new(self, name) + } + + /// Creates a `DebugTuple` builder designed to assist with creation of + /// `fmt::Debug` implementations for tuple structs. + /// + /// # Examples + /// + /// ```rust + /// 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) + /// .field(&self.1) + /// .finish() + /// } + /// } + /// + /// // prints "Foo(10, "Hello World")" + /// println!("{:?}", Foo(10, "Hello World".to_string())); + /// ``` + #[unstable(feature = "core", reason = "method was just created")] + pub fn debug_tuple<'b>(&'b mut self, name: &str) -> DebugTuple<'b, 'a> { + builders::debug_tuple_new(self, name) + } + + /// Creates a `DebugSet` builder designed to assist with creation of + /// `fmt::Debug` implementations for set-like structures. + /// + /// # Examples + /// + /// ```rust + /// use std::fmt; + /// + /// struct Foo(Vec); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// let mut builder = fmt.debug_set("Foo"); + /// for i in &self.0 { + /// builder = builder.entry(i); + /// } + /// builder.finish() + /// } + /// } + /// + /// // prints "Foo { 10, 11 }" + /// println!("{:?}", Foo(vec![10, 11])); + /// ``` + #[unstable(feature = "core", reason = "method was just created")] + pub fn debug_set<'b>(&'b mut self, name: &str) -> DebugSet<'b, 'a> { + builders::debug_set_new(self, name) + } + + /// Creates a `DebugMap` builder designed to assist with creation of + /// `fmt::Debug` implementations for map-like structures. + /// + /// # Examples + /// + /// ```rust + /// use std::fmt; + /// + /// struct Foo(Vec<(String, i32)>); + /// + /// impl fmt::Debug for Foo { + /// fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + /// let mut builder = fmt.debug_map("Foo"); + /// for &(ref key, ref value) in &self.0 { + /// builder = builder.entry(key, value); + /// } + /// builder.finish() + /// } + /// } + /// + /// // prints "Foo { "A": 10, "B": 11 }" + /// println!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11))); + /// ``` + #[unstable(feature = "core", reason = "method was just created")] + pub fn debug_map<'b>(&'b mut self, name: &str) -> DebugMap<'b, 'a> { + builders::debug_map_new(self, name) + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcoretest/fmt/builders.rs b/src/libcoretest/fmt/builders.rs new file mode 100644 index 000000000000..84076b349d2f --- /dev/null +++ b/src/libcoretest/fmt/builders.rs @@ -0,0 +1,391 @@ +mod debug_struct { + use std::fmt; + + #[test] + fn test_empty() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Foo").finish() + } + } + + assert_eq!("Foo", format!("{:?}", Foo)); + assert_eq!("Foo", format!("{:#?}", Foo)); + } + + #[test] + fn test_single() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Foo") + .field("bar", &true) + .finish() + } + } + + assert_eq!("Foo { bar: true }", format!("{:?}", Foo)); + assert_eq!( +"Foo { + bar: true +}", + format!("{:#?}", Foo)); + } + + #[test] + fn test_multiple() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Foo") + .field("bar", &true) + .field("baz", &format_args!("{}/{}", 10i32, 20i32)) + .finish() + } + } + + assert_eq!("Foo { bar: true, baz: 10/20 }", format!("{:?}", Foo)); + assert_eq!( +"Foo { + bar: true, + baz: 10/20 +}", + format!("{:#?}", Foo)); + } + + #[test] + fn test_nested() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Foo") + .field("bar", &true) + .field("baz", &format_args!("{}/{}", 10i32, 20i32)) + .finish() + } + } + + struct Bar; + + impl fmt::Debug for Bar { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Bar") + .field("foo", &Foo) + .field("hello", &"world") + .finish() + } + } + + assert_eq!("Bar { foo: Foo { bar: true, baz: 10/20 }, hello: \"world\" }", + format!("{:?}", Bar)); + assert_eq!( +"Bar { + foo: Foo { + bar: true, + baz: 10/20 + }, + hello: \"world\" +}", + format!("{:#?}", Bar)); + } +} + +mod debug_tuple { + use std::fmt; + + #[test] + fn test_empty() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_tuple("Foo").finish() + } + } + + assert_eq!("Foo", format!("{:?}", Foo)); + assert_eq!("Foo", format!("{:#?}", Foo)); + } + + #[test] + fn test_single() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_tuple("Foo") + .field(&true) + .finish() + } + } + + assert_eq!("Foo(true)", format!("{:?}", Foo)); + assert_eq!( +"Foo( + true +)", + format!("{:#?}", Foo)); + } + + #[test] + fn test_multiple() { + 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!("{}/{}", 10i32, 20i32)) + .finish() + } + } + + assert_eq!("Foo(true, 10/20)", format!("{:?}", Foo)); + assert_eq!( +"Foo( + true, + 10/20 +)", + format!("{:#?}", Foo)); + } + + #[test] + fn test_nested() { + 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!("{}/{}", 10i32, 20i32)) + .finish() + } + } + + 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() + } + } + + assert_eq!("Bar(Foo(true, 10/20), \"world\")", + format!("{:?}", Bar)); + assert_eq!( +"Bar( + Foo( + true, + 10/20 + ), + \"world\" +)", + format!("{:#?}", Bar)); + } +} + +mod debug_map { + use std::fmt; + + #[test] + fn test_empty() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_map("Foo").finish() + } + } + + assert_eq!("Foo {}", format!("{:?}", Foo)); + assert_eq!("Foo {}", format!("{:#?}", Foo)); + } + + #[test] + fn test_single() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_map("Foo") + .entry(&"bar", &true) + .finish() + } + } + + assert_eq!("Foo { \"bar\": true }", format!("{:?}", Foo)); + assert_eq!( +"Foo { + \"bar\": true +}", + format!("{:#?}", Foo)); + } + + #[test] + fn test_multiple() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_map("Foo") + .entry(&"bar", &true) + .entry(&10i32, &format_args!("{}/{}", 10i32, 20i32)) + .finish() + } + } + + assert_eq!("Foo { \"bar\": true, 10: 10/20 }", format!("{:?}", Foo)); + assert_eq!( +"Foo { + \"bar\": true, + 10: 10/20 +}", + format!("{:#?}", Foo)); + } + + #[test] + fn test_nested() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_map("Foo") + .entry(&"bar", &true) + .entry(&10i32, &format_args!("{}/{}", 10i32, 20i32)) + .finish() + } + } + + struct Bar; + + impl fmt::Debug for Bar { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_map("Bar") + .entry(&"foo", &Foo) + .entry(&Foo, &"world") + .finish() + } + } + + assert_eq!("Bar { \"foo\": Foo { \"bar\": true, 10: 10/20 }, \ + Foo { \"bar\": true, 10: 10/20 }: \"world\" }", + format!("{:?}", Bar)); + assert_eq!( +"Bar { + \"foo\": Foo { + \"bar\": true, + 10: 10/20 + }, + Foo { + \"bar\": true, + 10: 10/20 + }: \"world\" +}", + format!("{:#?}", Bar)); + } +} + +mod debug_set { + use std::fmt; + + #[test] + fn test_empty() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_set("Foo").finish() + } + } + + assert_eq!("Foo {}", format!("{:?}", Foo)); + assert_eq!("Foo {}", format!("{:#?}", Foo)); + } + + #[test] + fn test_single() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_set("Foo") + .entry(&true) + .finish() + } + } + + assert_eq!("Foo { true }", format!("{:?}", Foo)); + assert_eq!( +"Foo { + true +}", + format!("{:#?}", Foo)); + } + + #[test] + fn test_multiple() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_set("Foo") + .entry(&true) + .entry(&format_args!("{}/{}", 10i32, 20i32)) + .finish() + } + } + + assert_eq!("Foo { true, 10/20 }", format!("{:?}", Foo)); + assert_eq!( +"Foo { + true, + 10/20 +}", + format!("{:#?}", Foo)); + } + + #[test] + fn test_nested() { + struct Foo; + + impl fmt::Debug for Foo { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_set("Foo") + .entry(&true) + .entry(&format_args!("{}/{}", 10i32, 20i32)) + .finish() + } + } + + struct Bar; + + impl fmt::Debug for Bar { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_set("Bar") + .entry(&Foo) + .entry(&"world") + .finish() + } + } + + assert_eq!("Bar { Foo { true, 10/20 }, \"world\" }", + format!("{:?}", Bar)); + assert_eq!( +"Bar { + Foo { + true, + 10/20 + }, + \"world\" +}", + format!("{:#?}", Bar)); + } +} diff --git a/src/libcoretest/fmt/mod.rs b/src/libcoretest/fmt/mod.rs index e77920144468..cdb9c38f027f 100644 --- a/src/libcoretest/fmt/mod.rs +++ b/src/libcoretest/fmt/mod.rs @@ -9,6 +9,7 @@ // except according to those terms. mod num; +mod builders; #[test] fn test_format_flags() {