From e423fcf0e0166da55f88233e0be5eacba55bc0bc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 12 Dec 2014 10:59:41 -0800 Subject: [PATCH] std: Enforce Unicode in fmt::Writer This commit is an implementation of [RFC 526][rfc] which is a change to alter the definition of the old `fmt::FormatWriter`. The new trait, renamed to `Writer`, now only exposes one method `write_str` in order to guarantee that all implementations of the formatting traits can only produce valid Unicode. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0526-fmt-text-writer.md One of the primary improvements of this patch is the performance of the `.to_string()` method by avoiding an almost-always redundant UTF-8 check. This is a breaking change due to the renaming of the trait as well as the loss of the `write` method, but migration paths should be relatively easy: * All usage of `write` should move to `write_str`. If truly binary data was being written in an implementation of `Show`, then it will need to use a different trait or an altogether different code path. * All usage of `write!` should continue to work as-is with no modifications. * All usage of `Show` where implementations just delegate to another should continue to work as-is. [breaking-change] Closes #20352 --- src/libcollections/string.rs | 15 +- src/libcollections/vec.rs | 6 +- src/libcore/fmt/float.rs | 20 +- src/libcore/fmt/mod.rs | 71 ++--- src/libcore/fmt/num.rs | 4 +- src/librustc_driver/driver.rs | 12 +- src/librustdoc/html/escape.rs | 6 +- src/librustdoc/html/format.rs | 58 ++-- src/librustdoc/html/markdown.rs | 2 +- src/librustdoc/html/render.rs | 15 +- src/librustdoc/lib.rs | 13 +- src/libserialize/json.rs | 283 +++++++++--------- src/libstd/failure.rs | 6 +- src/libstd/fmt.rs | 23 +- src/libstd/io/mod.rs | 8 +- src/libstd/rt/unwind.rs | 19 +- src/libstd/rt/util.rs | 18 +- src/libsyntax/ast.rs | 3 +- src/libtest/lib.rs | 7 +- .../compile-fail/variance-trait-matching-2.rs | 10 +- src/test/run-fail/panic-non-utf8.rs | 26 -- src/test/run-pass/colorful-write-macros.rs | 10 +- src/test/run-pass/ifmt.rs | 24 +- src/test/run-pass/issue-11881.rs | 16 +- src/test/run-pass/issue-15924.rs | 4 +- 25 files changed, 320 insertions(+), 359 deletions(-) delete mode 100644 src/test/run-fail/panic-non-utf8.rs diff --git a/src/libcollections/string.rs b/src/libcollections/string.rs index 37a6e690f5d..74b698361f2 100644 --- a/src/libcollections/string.rs +++ b/src/libcollections/string.rs @@ -995,9 +995,11 @@ pub trait ToString { impl ToString for T { fn to_string(&self) -> String { - let mut buf = Vec::::new(); - let _ = fmt::write(&mut buf, format_args!("{}", *self)); - String::from_utf8(buf).unwrap() + use core::fmt::Writer; + let mut buf = String::new(); + let _ = buf.write_fmt(format_args!("{}", self)); + buf.shrink_to_fit(); + buf } } @@ -1073,6 +1075,13 @@ impl<'a> Str for CowString<'a> { } } +impl fmt::Writer for String { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.push_str(s); + Ok(()) + } +} + #[cfg(test)] mod tests { use prelude::*; diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index a1952352bad..a83e35a945d 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -1488,9 +1488,9 @@ impl fmt::Show for Vec { } } -impl<'a> fmt::FormatWriter for Vec { - fn write(&mut self, buf: &[u8]) -> fmt::Result { - self.push_all(buf); +impl<'a> fmt::Writer for Vec { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.push_all(s.as_bytes()); Ok(()) } } diff --git a/src/libcore/fmt/float.rs b/src/libcore/fmt/float.rs index e1728d762ed..a39168ec1ec 100644 --- a/src/libcore/fmt/float.rs +++ b/src/libcore/fmt/float.rs @@ -23,7 +23,7 @@ use num::FpCategory as Fp; use ops::FnOnce; use result::Result::Ok; use slice::{mod, SliceExt}; -use str::StrExt; +use str::{mod, StrExt}; /// A flag that specifies whether to use exponential (scientific) notation. pub enum ExponentFormat { @@ -95,7 +95,7 @@ pub fn float_to_str_bytes_common( exp_upper: bool, f: F ) -> U where - F: FnOnce(&[u8]) -> U, + F: FnOnce(&str) -> U, { assert!(2 <= radix && radix <= 36); match exp_format { @@ -109,12 +109,12 @@ pub fn float_to_str_bytes_common( let _1: T = Float::one(); match num.classify() { - Fp::Nan => return f("NaN".as_bytes()), + Fp::Nan => return f("NaN"), Fp::Infinite if num > _0 => { - return f("inf".as_bytes()); + return f("inf"); } Fp::Infinite if num < _0 => { - return f("-inf".as_bytes()); + return f("-inf"); } _ => {} } @@ -314,11 +314,11 @@ pub fn float_to_str_bytes_common( end: &'a mut uint, } - impl<'a> fmt::FormatWriter for Filler<'a> { - fn write(&mut self, bytes: &[u8]) -> fmt::Result { + impl<'a> fmt::Writer for Filler<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { slice::bytes::copy_memory(self.buf.slice_from_mut(*self.end), - bytes); - *self.end += bytes.len(); + s.as_bytes()); + *self.end += s.len(); Ok(()) } } @@ -332,5 +332,5 @@ pub fn float_to_str_bytes_common( } } - f(buf[..end]) + f(unsafe { str::from_utf8_unchecked(buf[..end]) }) } diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 87fcb12e29f..f2439d515b4 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -24,7 +24,7 @@ use result::Result::{Ok, Err}; use result; use slice::SliceExt; use slice; -use str::{StrExt, Utf8Error}; +use str::{mod, StrExt, Utf8Error}; pub use self::num::radix; pub use self::num::Radix; @@ -57,7 +57,7 @@ pub struct Error; /// library. The `write!` macro accepts an instance of `io::Writer`, and the /// `io::Writer` trait is favored over implementing this trait. #[experimental = "waiting for core and I/O reconciliation"] -pub trait FormatWriter { +pub trait Writer { /// Writes a slice of bytes into this writer, returning whether the write /// succeeded. /// @@ -68,7 +68,7 @@ pub trait FormatWriter { /// # Errors /// /// This function will return an instance of `FormatError` on error. - fn write(&mut self, bytes: &[u8]) -> Result; + fn write_str(&mut self, s: &str) -> Result; /// Glue for usage of the `write!` macro with implementers of this trait. /// @@ -88,7 +88,7 @@ pub struct Formatter<'a> { width: Option, precision: Option, - buf: &'a mut (FormatWriter+'a), + buf: &'a mut (Writer+'a), curarg: slice::Iter<'a, Argument<'a>>, args: &'a [Argument<'a>], } @@ -258,17 +258,6 @@ pub trait UpperExp for Sized? { fn fmt(&self, &mut Formatter) -> Result; } -static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument { - position: rt::ArgumentNext, - format: rt::FormatSpec { - fill: ' ', - align: rt::AlignUnknown, - flags: 0, - precision: rt::CountImplied, - width: rt::CountImplied, - } -}; - /// The `write` function takes an output stream, a precompiled format string, /// and a list of arguments. The arguments will be formatted according to the /// specified format string into the output stream provided. @@ -279,7 +268,7 @@ static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument { /// * args - the precompiled arguments generated by `format_args!` #[experimental = "libcore and I/O have yet to be reconciled, and this is an \ implementation detail which should not otherwise be exported"] -pub fn write(output: &mut FormatWriter, args: Arguments) -> Result { +pub fn write(output: &mut Writer, args: Arguments) -> Result { let mut formatter = Formatter { flags: 0, width: None, @@ -296,16 +285,16 @@ pub fn write(output: &mut FormatWriter, args: Arguments) -> Result { match args.fmt { None => { // We can use default formatting parameters for all arguments. - for _ in range(0, args.args.len()) { - try!(formatter.buf.write(pieces.next().unwrap().as_bytes())); - try!(formatter.run(&DEFAULT_ARGUMENT)); + for (arg, piece) in args.args.iter().zip(pieces.by_ref()) { + try!(formatter.buf.write_str(*piece)); + try!((arg.formatter)(arg.value, &mut formatter)); } } Some(fmt) => { // Every spec has a corresponding argument that is preceded by // a string piece. for (arg, piece) in fmt.iter().zip(pieces.by_ref()) { - try!(formatter.buf.write(piece.as_bytes())); + try!(formatter.buf.write_str(*piece)); try!(formatter.run(arg)); } } @@ -314,7 +303,7 @@ pub fn write(output: &mut FormatWriter, args: Arguments) -> Result { // There can be only one trailing string piece left. match pieces.next() { Some(piece) => { - try!(formatter.buf.write(piece.as_bytes())); + try!(formatter.buf.write_str(*piece)); } None => {} } @@ -378,7 +367,7 @@ impl<'a> Formatter<'a> { pub fn pad_integral(&mut self, is_positive: bool, prefix: &str, - buf: &[u8]) + buf: &str) -> Result { use char::Char; use fmt::rt::{FlagAlternate, FlagSignPlus, FlagSignAwareZeroPad}; @@ -402,9 +391,10 @@ impl<'a> Formatter<'a> { for c in sign.into_iter() { let mut b = [0; 4]; let n = c.encode_utf8(&mut b).unwrap_or(0); - try!(f.buf.write(b[..n])); + let b = unsafe { str::from_utf8_unchecked(b[0..n]) }; + try!(f.buf.write_str(b)); } - if prefixed { f.buf.write(prefix.as_bytes()) } + if prefixed { f.buf.write_str(prefix) } else { Ok(()) } }; @@ -413,24 +403,26 @@ impl<'a> Formatter<'a> { // If there's no minimum length requirements then we can just // write the bytes. None => { - try!(write_prefix(self)); self.buf.write(buf) + try!(write_prefix(self)); self.buf.write_str(buf) } // Check if we're over the minimum width, if so then we can also // just write the bytes. Some(min) if width >= min => { - try!(write_prefix(self)); self.buf.write(buf) + try!(write_prefix(self)); self.buf.write_str(buf) } // The sign and prefix goes before the padding if the fill character // is zero Some(min) if self.flags & (1 << (FlagSignAwareZeroPad as uint)) != 0 => { self.fill = '0'; try!(write_prefix(self)); - self.with_padding(min - width, rt::AlignRight, |f| f.buf.write(buf)) + self.with_padding(min - width, rt::AlignRight, |f| { + f.buf.write_str(buf) + }) } // Otherwise, the sign and prefix goes after the padding Some(min) => { self.with_padding(min - width, rt::AlignRight, |f| { - try!(write_prefix(f)); f.buf.write(buf) + try!(write_prefix(f)); f.buf.write_str(buf) }) } } @@ -451,7 +443,7 @@ impl<'a> Formatter<'a> { pub fn pad(&mut self, s: &str) -> Result { // Make sure there's a fast path up front if self.width.is_none() && self.precision.is_none() { - return self.buf.write(s.as_bytes()); + return self.buf.write_str(s); } // The `precision` field can be interpreted as a `max-width` for the // string being formatted @@ -463,7 +455,7 @@ impl<'a> Formatter<'a> { let char_len = s.char_len(); if char_len >= max { let nchars = ::cmp::min(max, char_len); - return self.buf.write(s.slice_chars(0, nchars).as_bytes()); + return self.buf.write_str(s.slice_chars(0, nchars)); } } None => {} @@ -472,17 +464,17 @@ impl<'a> Formatter<'a> { match self.width { // If we're under the maximum length, and there's no minimum length // requirements, then we can just emit the string - None => self.buf.write(s.as_bytes()), + None => self.buf.write_str(s), // If we're under the maximum width, check if we're over the minimum // width, if so it's as easy as just emitting the string. Some(width) if s.char_len() >= width => { - self.buf.write(s.as_bytes()) + self.buf.write_str(s) } // If we're under both the maximum and the minimum width, then fill // up the minimum width with the specified string + some alignment. Some(width) => { self.with_padding(width - s.char_len(), rt::AlignLeft, |me| { - me.buf.write(s.as_bytes()) + me.buf.write_str(s) }) } } @@ -507,15 +499,16 @@ impl<'a> Formatter<'a> { let mut fill = [0u8; 4]; let len = self.fill.encode_utf8(&mut fill).unwrap_or(0); + let fill = unsafe { str::from_utf8_unchecked(fill[..len]) }; for _ in range(0, pre_pad) { - try!(self.buf.write(fill[..len])); + try!(self.buf.write_str(fill)); } try!(f(self)); for _ in range(0, post_pad) { - try!(self.buf.write(fill[..len])); + try!(self.buf.write_str(fill)); } Ok(()) @@ -524,8 +517,8 @@ impl<'a> Formatter<'a> { /// Writes some data to the underlying buffer contained within this /// formatter. #[unstable = "reconciling core and I/O may alter this definition"] - pub fn write(&mut self, data: &[u8]) -> Result { - self.buf.write(data) + pub fn write_str(&mut self, data: &str) -> Result { + self.buf.write_str(data) } /// Writes some formatted information into this instance @@ -616,7 +609,9 @@ impl Show for char { impl Pointer for *const T { fn fmt(&self, f: &mut Formatter) -> Result { f.flags |= 1 << (rt::FlagAlternate as uint); - LowerHex::fmt(&(*self as uint), f) + let ret = LowerHex::fmt(&(*self as uint), f); + f.flags &= !(1 << (rt::FlagAlternate as uint)); + ret } } diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index 7de3e847dc6..4f0cecbb243 100644 --- a/src/libcore/fmt/num.rs +++ b/src/libcore/fmt/num.rs @@ -18,6 +18,7 @@ use fmt; use iter::DoubleEndedIteratorExt; use num::{Int, cast}; use slice::SliceExt; +use str; /// A type that represents a specific radix #[doc(hidden)] @@ -60,7 +61,8 @@ trait GenericRadix { if x == zero { break }; // No more digits left to accumulate. } } - f.pad_integral(is_positive, self.prefix(), buf[curr..]) + let buf = unsafe { str::from_utf8_unchecked(buf[curr..]) }; + f.pad_integral(is_positive, self.prefix(), buf) } } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 91902b90673..a92c2fe2ccb 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -28,7 +28,7 @@ use rustc_trans::save; use rustc_trans::trans; use rustc_typeck as typeck; -use serialize::{json, Encodable}; +use serialize::json; use std::io; use std::io::fs; @@ -143,10 +143,7 @@ pub fn phase_1_parse_input(sess: &Session, cfg: ast::CrateConfig, input: &Input) }); if sess.opts.debugging_opts & config::AST_JSON_NOEXPAND != 0 { - let mut stdout = io::BufferedWriter::new(io::stdout()); - let mut json = json::PrettyEncoder::new(&mut stdout); - // unwrapping so IoError isn't ignored - krate.encode(&mut json).unwrap(); + println!("{}", json::as_json(&krate)); } if sess.show_span() { @@ -338,10 +335,7 @@ pub fn assign_node_ids_and_map<'ast>(sess: &Session, ast_map::map_crate(forest, NodeIdAssigner { sess: sess })); if sess.opts.debugging_opts & config::AST_JSON != 0 { - let mut stdout = io::BufferedWriter::new(io::stdout()); - let mut json = json::PrettyEncoder::new(&mut stdout); - // unwrapping so IoError isn't ignored - map.krate().encode(&mut json).unwrap(); + println!("{}", json::as_json(map.krate())); } map diff --git a/src/librustdoc/html/escape.rs b/src/librustdoc/html/escape.rs index fe93dbbc081..b4afb67170b 100644 --- a/src/librustdoc/html/escape.rs +++ b/src/librustdoc/html/escape.rs @@ -29,7 +29,7 @@ impl<'a> fmt::Show for Escape<'a> { for (i, ch) in s.bytes().enumerate() { match ch as char { '<' | '>' | '&' | '\'' | '"' => { - try!(fmt.write(pile_o_bits.slice(last, i).as_bytes())); + try!(fmt.write_str(pile_o_bits.slice(last, i))); let s = match ch as char { '>' => ">", '<' => "<", @@ -38,7 +38,7 @@ impl<'a> fmt::Show for Escape<'a> { '"' => """, _ => unreachable!() }; - try!(fmt.write(s.as_bytes())); + try!(fmt.write_str(s)); last = i + 1; } _ => {} @@ -46,7 +46,7 @@ impl<'a> fmt::Show for Escape<'a> { } if last < s.len() { - try!(fmt.write(pile_o_bits.slice_from(last).as_bytes())); + try!(fmt.write_str(pile_o_bits.slice_from(last))); } Ok(()) } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 3c09a10f3d9..36619566f8c 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -69,7 +69,7 @@ impl<'a> fmt::Show for TyParamBounds<'a> { let &TyParamBounds(bounds) = self; for (i, bound) in bounds.iter().enumerate() { if i > 0 { - try!(f.write(" + ".as_bytes())); + try!(f.write_str(" + ")); } try!(write!(f, "{}", *bound)); } @@ -80,24 +80,24 @@ impl<'a> fmt::Show for TyParamBounds<'a> { impl fmt::Show for clean::Generics { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.lifetimes.len() == 0 && self.type_params.len() == 0 { return Ok(()) } - try!(f.write("<".as_bytes())); + try!(f.write_str("<")); for (i, life) in self.lifetimes.iter().enumerate() { if i > 0 { - try!(f.write(", ".as_bytes())); + try!(f.write_str(", ")); } try!(write!(f, "{}", *life)); } if self.type_params.len() > 0 { if self.lifetimes.len() > 0 { - try!(f.write(", ".as_bytes())); + try!(f.write_str(", ")); } for (i, tp) in self.type_params.iter().enumerate() { if i > 0 { - try!(f.write(", ".as_bytes())) + try!(f.write_str(", ")) } - try!(f.write(tp.name.as_bytes())); + try!(f.write_str(tp.name[])); if tp.bounds.len() > 0 { try!(write!(f, ": {}", TyParamBounds(tp.bounds.as_slice()))); @@ -109,7 +109,7 @@ impl fmt::Show for clean::Generics { }; } } - try!(f.write(">".as_bytes())); + try!(f.write_str(">")); Ok(()) } } @@ -120,10 +120,10 @@ impl<'a> fmt::Show for WhereClause<'a> { if gens.where_predicates.len() == 0 { return Ok(()); } - try!(f.write(" where ".as_bytes())); + try!(f.write_str(" where ")); for (i, pred) in gens.where_predicates.iter().enumerate() { if i > 0 { - try!(f.write(", ".as_bytes())); + try!(f.write_str(", ")); } match pred { &clean::WherePredicate::BoundPredicate { ref ty, ref bounds } => { @@ -135,7 +135,7 @@ impl<'a> fmt::Show for WhereClause<'a> { try!(write!(f, "{}: ", lifetime)); for (i, lifetime) in bounds.iter().enumerate() { if i > 0 { - try!(f.write(" + ".as_bytes())); + try!(f.write_str(" + ")); } try!(write!(f, "{}", lifetime)); @@ -146,14 +146,14 @@ impl<'a> fmt::Show for WhereClause<'a> { } } } - try!(f.write("".as_bytes())); + try!(f.write_str("")); Ok(()) } } impl fmt::Show for clean::Lifetime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(f.write(self.get_ref().as_bytes())); + try!(f.write_str(self.get_ref())); Ok(()) } } @@ -161,14 +161,14 @@ impl fmt::Show for clean::Lifetime { impl fmt::Show for clean::PolyTrait { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.lifetimes.len() > 0 { - try!(f.write("for<".as_bytes())); + try!(f.write_str("for<")); for (i, lt) in self.lifetimes.iter().enumerate() { if i > 0 { - try!(f.write(", ".as_bytes())); + try!(f.write_str(", ")); } try!(write!(f, "{}", lt)); } - try!(f.write("> ".as_bytes())); + try!(f.write_str("> ")); } write!(f, "{}", self.trait_) } @@ -196,38 +196,38 @@ impl fmt::Show for clean::PathParameters { match *self { clean::PathParameters::AngleBracketed { ref lifetimes, ref types } => { if lifetimes.len() > 0 || types.len() > 0 { - try!(f.write("<".as_bytes())); + try!(f.write_str("<")); let mut comma = false; for lifetime in lifetimes.iter() { if comma { - try!(f.write(", ".as_bytes())); + try!(f.write_str(", ")); } comma = true; try!(write!(f, "{}", *lifetime)); } for ty in types.iter() { if comma { - try!(f.write(", ".as_bytes())); + try!(f.write_str(", ")); } comma = true; try!(write!(f, "{}", *ty)); } - try!(f.write(">".as_bytes())); + try!(f.write_str(">")); } } clean::PathParameters::Parenthesized { ref inputs, ref output } => { - try!(f.write("(".as_bytes())); + try!(f.write_str("(")); let mut comma = false; for ty in inputs.iter() { if comma { - try!(f.write(", ".as_bytes())); + try!(f.write_str(", ")); } comma = true; try!(write!(f, "{}", *ty)); } - try!(f.write(")".as_bytes())); + try!(f.write_str(")")); if let Some(ref ty) = *output { - try!(f.write(" -> ".as_bytes())); + try!(f.write_str(" -> ")); try!(write!(f, "{}", ty)); } } @@ -238,7 +238,7 @@ impl fmt::Show for clean::PathParameters { impl fmt::Show for clean::PathSegment { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(f.write(self.name.as_bytes())); + try!(f.write_str(self.name.as_slice())); write!(f, "{}", self.params) } } @@ -246,12 +246,12 @@ impl fmt::Show for clean::PathSegment { impl fmt::Show for clean::Path { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.global { - try!(f.write("::".as_bytes())) + try!(f.write_str("::")) } for (i, seg) in self.segments.iter().enumerate() { if i > 0 { - try!(f.write("::".as_bytes())) + try!(f.write_str("::")) } try!(write!(f, "{}", seg)); } @@ -433,10 +433,10 @@ impl fmt::Show for clean::Type { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { clean::TyParamBinder(id) => { - f.write(cache().typarams[ast_util::local_def(id)].as_bytes()) + f.write_str(cache().typarams[ast_util::local_def(id)][]) } clean::Generic(ref name) => { - f.write(name.as_bytes()) + f.write_str(name.as_slice()) } clean::ResolvedPath{ did, ref typarams, ref path } => { try!(resolved_path(f, did, path, false)); @@ -522,7 +522,7 @@ impl fmt::Show for clean::Type { primitive_link(f, clean::Slice, format!("[{}, ..{}]", **t, *s).as_slice()) } - clean::Bottom => f.write("!".as_bytes()), + clean::Bottom => f.write_str("!"), clean::RawPointer(m, ref t) => { write!(f, "*{}{}", RawMutableSpace(m), **t) } diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 2c05524ea7f..fd7145552c4 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -302,7 +302,7 @@ pub fn render(w: &mut fmt::Formatter, s: &str, print_toc: bool) -> fmt::Result { if ret.is_ok() { let buf = slice::from_raw_buf(&(*ob).data, (*ob).size as uint); - ret = w.write(buf); + ret = w.write_str(str::from_utf8(buf).unwrap()); } hoedown_buffer_free(ob); ret diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index f8a0b88b408..c3cc61c7990 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -49,7 +49,6 @@ use std::sync::Arc; use externalfiles::ExternalHtml; use serialize::json; -use serialize::Encodable; use serialize::json::ToJson; use syntax::ast; use syntax::ast_util; @@ -1095,7 +1094,7 @@ impl Context { try!(self.recurse(stability.name.clone(), |this| { let json_dst = &this.dst.join("stability.json"); let mut json_out = BufferedWriter::new(try!(File::create(json_dst))); - try!(stability.encode(&mut json::Encoder::new(&mut json_out))); + try!(write!(&mut json_out, "{}", json::as_json(&stability))); let mut title = stability.name.clone(); title.push_str(" - Stability dashboard"); @@ -1311,7 +1310,8 @@ impl<'a> Item<'a> { // has anchors for the line numbers that we're linking to. if ast_util::is_local(self.item.def_id) { let mut path = Vec::new(); - clean_srcpath(&cx.src_root, self.item.source.filename.as_bytes(), |component| { + clean_srcpath(&cx.src_root, self.item.source.filename.as_bytes(), + |component| { path.push(component.to_string()); }); let href = if self.item.source.loline == self.item.source.hiline { @@ -1713,7 +1713,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, try!(write!(w, ";\n")); } if types.len() > 0 && required.len() > 0 { - try!(w.write("\n".as_bytes())); + try!(w.write_str("\n")); } for m in required.iter() { try!(write!(w, " ")); @@ -1721,7 +1721,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, try!(write!(w, ";\n")); } if required.len() > 0 && provided.len() > 0 { - try!(w.write("\n".as_bytes())); + try!(w.write_str("\n")); } for m in provided.iter() { try!(write!(w, " ")); @@ -2260,8 +2260,9 @@ impl<'a> fmt::Show for Source<'a> { fn item_macro(w: &mut fmt::Formatter, it: &clean::Item, t: &clean::Macro) -> fmt::Result { - try!(w.write(highlight::highlight(t.source.as_slice(), Some("macro"), - None).as_bytes())); + try!(w.write_str(highlight::highlight(t.source.as_slice(), + Some("macro"), + None)[])); document(w, it) } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index ccdc8164255..8eb4448c649 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -41,7 +41,7 @@ use std::io::File; use std::io; use std::rc::Rc; use externalfiles::ExternalHtml; -use serialize::{Decodable, Encodable}; +use serialize::Decodable; use serialize::json::{mod, Json}; use rustc::session::search_paths::SearchPaths; @@ -493,14 +493,7 @@ fn json_output(krate: clean::Crate, res: Vec , // FIXME #8335: yuck, Rust -> str -> JSON round trip! No way to .encode // straight to the Rust JSON representation. - let crate_json_str = { - let mut w = Vec::new(); - { - let mut encoder = json::Encoder::new(&mut w as &mut io::Writer); - krate.encode(&mut encoder).unwrap(); - } - String::from_utf8(w).unwrap() - }; + let crate_json_str = format!("{}", json::as_json(&krate)); let crate_json = match json::from_str(crate_json_str.as_slice()) { Ok(j) => j, Err(e) => panic!("Rust generated JSON is invalid: {}", e) @@ -510,5 +503,5 @@ fn json_output(krate: clean::Crate, res: Vec , json.insert("plugins".to_string(), Json::Object(plugins_json)); let mut file = try!(File::create(&dst)); - Json::Object(json).to_writer(&mut file) + write!(&mut file, "{}", Json::Object(json)) } diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index 8a9c2eebf3a..2c83807aa4f 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -227,6 +227,11 @@ pub enum Json { pub type Array = Vec; pub type Object = BTreeMap; +pub struct PrettyJson<'a> { inner: &'a Json } + +pub struct AsJson<'a, T: 'a> { inner: &'a T } +pub struct AsPrettyJson<'a, T: 'a> { inner: &'a T, indent: Option } + /// The errors that can arise while parsing a JSON stream. #[deriving(Clone, Copy, PartialEq)] pub enum ErrorCode { @@ -303,9 +308,15 @@ pub fn decode>(s: &str) -> DecodeResult } /// Shortcut function to encode a `T` into a JSON `String` -pub fn encode<'a, T: Encodable, io::IoError>>(object: &T) -> string::String { - let buff = Encoder::buffer_encode(object); - string::String::from_utf8(buff).unwrap() +pub fn encode(object: &T) -> string::String + where T: for<'a> Encodable, fmt::Error> +{ + let mut s = String::new(); + { + let mut encoder = Encoder::new(&mut s); + let _ = object.encode(&mut encoder); + } + s } impl fmt::Show for ErrorCode { @@ -323,16 +334,16 @@ impl std::error::Error for DecoderError { fn detail(&self) -> Option { Some(self.to_string()) } } -pub type EncodeResult = io::IoResult<()>; +pub type EncodeResult = fmt::Result; pub type DecodeResult = Result; -pub fn escape_bytes(wr: &mut io::Writer, bytes: &[u8]) -> Result<(), io::IoError> { +fn escape_str(wr: &mut fmt::Writer, v: &str) -> fmt::Result { try!(wr.write_str("\"")); let mut start = 0; - for (i, byte) in bytes.iter().enumerate() { - let escaped = match *byte { + for (i, byte) in v.bytes().enumerate() { + let escaped = match byte { b'"' => "\\\"", b'\\' => "\\\\", b'\x00' => "\\u0000", @@ -372,7 +383,7 @@ pub fn escape_bytes(wr: &mut io::Writer, bytes: &[u8]) -> Result<(), io::IoError }; if start < i { - try!(wr.write(bytes[start..i])); + try!(wr.write_str(v[start..i])); } try!(wr.write_str(escaped)); @@ -380,34 +391,30 @@ pub fn escape_bytes(wr: &mut io::Writer, bytes: &[u8]) -> Result<(), io::IoError start = i + 1; } - if start != bytes.len() { - try!(wr.write(bytes[start..])); + if start != v.len() { + try!(wr.write_str(v[start..])); } wr.write_str("\"") } -fn escape_str(writer: &mut io::Writer, v: &str) -> Result<(), io::IoError> { - escape_bytes(writer, v.as_bytes()) +fn escape_char(writer: &mut fmt::Writer, v: char) -> fmt::Result { + let mut buf = [0, .. 4]; + let n = v.encode_utf8(&mut buf).unwrap(); + let buf = unsafe { str::from_utf8_unchecked(buf[0..n]) }; + escape_str(writer, buf) } -fn escape_char(writer: &mut io::Writer, v: char) -> Result<(), io::IoError> { - let mut buf = [0; 4]; - let len = v.encode_utf8(&mut buf).unwrap(); - escape_bytes(writer, buf[mut ..len]) -} +fn spaces(wr: &mut fmt::Writer, mut n: uint) -> fmt::Result { + const BUF: &'static str = " "; -fn spaces(wr: &mut io::Writer, mut n: uint) -> Result<(), io::IoError> { - const LEN: uint = 16; - static BUF: [u8; LEN] = [b' '; LEN]; - - while n >= LEN { - try!(wr.write(&BUF)); - n -= LEN; + while n >= BUF.len() { + try!(wr.write_str(BUF)); + n -= BUF.len(); } if n > 0 { - wr.write(BUF[..n]) + wr.write_str(BUF[..n]) } else { Ok(()) } @@ -423,31 +430,18 @@ fn fmt_number_or_null(v: f64) -> string::String { /// A structure for implementing serialization to JSON. pub struct Encoder<'a> { - writer: &'a mut (io::Writer+'a), + writer: &'a mut (fmt::Writer+'a), } impl<'a> Encoder<'a> { /// Creates a new JSON encoder whose output will be written to the writer /// specified. - pub fn new(writer: &'a mut io::Writer) -> Encoder<'a> { + pub fn new(writer: &'a mut fmt::Writer) -> Encoder<'a> { Encoder { writer: writer } } - - /// Encode the specified struct into a json [u8] - pub fn buffer_encode, io::IoError>>(object: &T) -> Vec { - //Serialize the object in a string using a writer - let mut m = Vec::new(); - // FIXME(14302) remove the transmute and unsafe block. - unsafe { - let mut encoder = Encoder::new(&mut m as &mut io::Writer); - // Vec never Errs - let _ = object.encode(transmute(&mut encoder)); - } - m - } } -impl<'a> ::Encoder for Encoder<'a> { +impl<'a> ::Encoder for Encoder<'a> { fn emit_nil(&mut self) -> EncodeResult { write!(self.writer, "null") } fn emit_uint(&mut self, v: uint) -> EncodeResult { write!(self.writer, "{}", v) } @@ -646,14 +640,14 @@ impl<'a> ::Encoder for Encoder<'a> { /// Another encoder for JSON, but prints out human-readable JSON instead of /// compact data pub struct PrettyEncoder<'a> { - writer: &'a mut (io::Writer+'a), + writer: &'a mut (fmt::Writer+'a), curr_indent: uint, indent: uint, } impl<'a> PrettyEncoder<'a> { /// Creates a new encoder whose output will be written to the specified writer - pub fn new(writer: &'a mut io::Writer) -> PrettyEncoder<'a> { + pub fn new(writer: &'a mut fmt::Writer) -> PrettyEncoder<'a> { PrettyEncoder { writer: writer, curr_indent: 0, indent: 2, } } @@ -667,7 +661,7 @@ impl<'a> PrettyEncoder<'a> { } } -impl<'a> ::Encoder for PrettyEncoder<'a> { +impl<'a> ::Encoder for PrettyEncoder<'a> { fn emit_nil(&mut self) -> EncodeResult { write!(self.writer, "null") } fn emit_uint(&mut self, v: uint) -> EncodeResult { write!(self.writer, "{}", v) } @@ -927,25 +921,23 @@ impl, S> Encodable for Json { } } +/// Create an `AsJson` wrapper which can be used to print a value as JSON +/// on-the-fly via `write!` +pub fn as_json(t: &T) -> AsJson { + AsJson { inner: t } +} + +/// Create an `AsPrettyJson` wrapper which can be used to print a value as JSON +/// on-the-fly via `write!` +pub fn as_pretty_json(t: &T) -> AsPrettyJson { + AsPrettyJson { inner: t, indent: None } +} + impl Json { - /// Encodes a json value into an io::writer. Uses a single line. - pub fn to_writer(&self, writer: &mut io::Writer) -> EncodeResult { - let mut encoder = Encoder::new(writer); - self.encode(&mut encoder) - } - - /// Encodes a json value into an io::writer. - /// Pretty-prints in a more readable format. - pub fn to_pretty_writer(&self, writer: &mut io::Writer) -> EncodeResult { - let mut encoder = PrettyEncoder::new(writer); - self.encode(&mut encoder) - } - - /// Encodes a json value into a string - pub fn to_pretty_str(&self) -> string::String { - let mut s = Vec::new(); - self.to_pretty_writer(&mut s as &mut io::Writer).unwrap(); - string::String::from_utf8(s).unwrap() + /// Borrow this json object as a pretty object to generate a pretty + /// representation for it via `Show`. + pub fn pretty(&self) -> PrettyJson { + PrettyJson { inner: self } } /// If the Json value is an Object, returns the value associated with the provided key. @@ -2424,10 +2416,65 @@ impl ToJson for Option { } } +struct FormatShim<'a, 'b: 'a> { + inner: &'a mut fmt::Formatter<'b>, +} + +impl<'a, 'b> fmt::Writer for FormatShim<'a, 'b> { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.inner.write_str(s) + } +} + impl fmt::Show for Json { /// Encodes a json value into a string fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.to_writer(f).map_err(|_| fmt::Error) + let mut shim = FormatShim { inner: f }; + let mut encoder = Encoder::new(&mut shim); + self.encode(&mut encoder) + } +} + +impl<'a> fmt::Show for PrettyJson<'a> { + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = PrettyEncoder::new(&mut shim); + self.inner.encode(&mut encoder) + } +} + +impl<'a, T> fmt::Show for AsJson<'a, T> + where T: for<'b> Encodable, fmt::Error> +{ + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = Encoder::new(&mut shim); + self.inner.encode(&mut encoder) + } +} + +impl<'a, T> AsPrettyJson<'a, T> { + /// Set the indentation level for the emitted JSON + pub fn indent(mut self, indent: uint) -> AsPrettyJson<'a, T> { + self.indent = Some(indent); + self + } +} + +impl<'a, T> fmt::Show for AsPrettyJson<'a, T> + where T: for<'b> Encodable, fmt::Error> +{ + /// Encodes a json value into a string + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut shim = FormatShim { inner: f }; + let mut encoder = PrettyEncoder::new(&mut shim); + match self.indent { + Some(n) => encoder.set_indent(n), + None => {} + } + self.inner.encode(&mut encoder) } } @@ -2450,9 +2497,9 @@ mod tests { use super::DecoderError::*; use super::JsonEvent::*; use super::StackElement::*; - use super::{PrettyEncoder, Json, from_str, DecodeResult, DecoderError, JsonEvent, Parser, - StackElement, Stack, Encoder, Decoder}; - use std::{i64, u64, f32, f64, io}; + use super::{Json, from_str, DecodeResult, DecoderError, JsonEvent, Parser, + StackElement, Stack, Decoder}; + use std::{i64, u64, f32, f64}; use std::collections::BTreeMap; use std::num::Float; use std::string; @@ -2523,74 +2570,74 @@ mod tests { #[test] fn test_write_null() { assert_eq!(Null.to_string(), "null"); - assert_eq!(Null.to_pretty_str(), "null"); + assert_eq!(Null.pretty().to_string(), "null"); } #[test] fn test_write_i64() { assert_eq!(U64(0).to_string(), "0"); - assert_eq!(U64(0).to_pretty_str(), "0"); + assert_eq!(U64(0).pretty().to_string(), "0"); assert_eq!(U64(1234).to_string(), "1234"); - assert_eq!(U64(1234).to_pretty_str(), "1234"); + assert_eq!(U64(1234).pretty().to_string(), "1234"); assert_eq!(I64(-5678).to_string(), "-5678"); - assert_eq!(I64(-5678).to_pretty_str(), "-5678"); + assert_eq!(I64(-5678).pretty().to_string(), "-5678"); assert_eq!(U64(7650007200025252000).to_string(), "7650007200025252000"); - assert_eq!(U64(7650007200025252000).to_pretty_str(), "7650007200025252000"); + assert_eq!(U64(7650007200025252000).pretty().to_string(), "7650007200025252000"); } #[test] fn test_write_f64() { assert_eq!(F64(3.0).to_string(), "3.0"); - assert_eq!(F64(3.0).to_pretty_str(), "3.0"); + assert_eq!(F64(3.0).pretty().to_string(), "3.0"); assert_eq!(F64(3.1).to_string(), "3.1"); - assert_eq!(F64(3.1).to_pretty_str(), "3.1"); + assert_eq!(F64(3.1).pretty().to_string(), "3.1"); assert_eq!(F64(-1.5).to_string(), "-1.5"); - assert_eq!(F64(-1.5).to_pretty_str(), "-1.5"); + assert_eq!(F64(-1.5).pretty().to_string(), "-1.5"); assert_eq!(F64(0.5).to_string(), "0.5"); - assert_eq!(F64(0.5).to_pretty_str(), "0.5"); + assert_eq!(F64(0.5).pretty().to_string(), "0.5"); assert_eq!(F64(f64::NAN).to_string(), "null"); - assert_eq!(F64(f64::NAN).to_pretty_str(), "null"); + assert_eq!(F64(f64::NAN).pretty().to_string(), "null"); assert_eq!(F64(f64::INFINITY).to_string(), "null"); - assert_eq!(F64(f64::INFINITY).to_pretty_str(), "null"); + assert_eq!(F64(f64::INFINITY).pretty().to_string(), "null"); assert_eq!(F64(f64::NEG_INFINITY).to_string(), "null"); - assert_eq!(F64(f64::NEG_INFINITY).to_pretty_str(), "null"); + assert_eq!(F64(f64::NEG_INFINITY).pretty().to_string(), "null"); } #[test] fn test_write_str() { assert_eq!(String("".to_string()).to_string(), "\"\""); - assert_eq!(String("".to_string()).to_pretty_str(), "\"\""); + assert_eq!(String("".to_string()).pretty().to_string(), "\"\""); assert_eq!(String("homura".to_string()).to_string(), "\"homura\""); - assert_eq!(String("madoka".to_string()).to_pretty_str(), "\"madoka\""); + assert_eq!(String("madoka".to_string()).pretty().to_string(), "\"madoka\""); } #[test] fn test_write_bool() { assert_eq!(Boolean(true).to_string(), "true"); - assert_eq!(Boolean(true).to_pretty_str(), "true"); + assert_eq!(Boolean(true).pretty().to_string(), "true"); assert_eq!(Boolean(false).to_string(), "false"); - assert_eq!(Boolean(false).to_pretty_str(), "false"); + assert_eq!(Boolean(false).pretty().to_string(), "false"); } #[test] fn test_write_array() { assert_eq!(Array(vec![]).to_string(), "[]"); - assert_eq!(Array(vec![]).to_pretty_str(), "[]"); + assert_eq!(Array(vec![]).pretty().to_string(), "[]"); assert_eq!(Array(vec![Boolean(true)]).to_string(), "[true]"); assert_eq!( - Array(vec![Boolean(true)]).to_pretty_str(), + Array(vec![Boolean(true)]).pretty().to_string(), "\ [\n \ true\n\ @@ -2605,7 +2652,7 @@ mod tests { assert_eq!(long_test_array.to_string(), "[false,null,[\"foo\\nbar\",3.5]]"); assert_eq!( - long_test_array.to_pretty_str(), + long_test_array.pretty().to_string(), "\ [\n \ false,\n \ @@ -2621,7 +2668,7 @@ mod tests { #[test] fn test_write_object() { assert_eq!(mk_object(&[]).to_string(), "{}"); - assert_eq!(mk_object(&[]).to_pretty_str(), "{}"); + assert_eq!(mk_object(&[]).pretty().to_string(), "{}"); assert_eq!( mk_object(&[ @@ -2630,7 +2677,7 @@ mod tests { "{\"a\":true}" ); assert_eq!( - mk_object(&[("a".to_string(), Boolean(true))]).to_pretty_str(), + mk_object(&[("a".to_string(), Boolean(true))]).pretty().to_string(), "\ {\n \ \"a\": true\n\ @@ -2654,7 +2701,7 @@ mod tests { }" ); assert_eq!( - complex_obj.to_pretty_str(), + complex_obj.pretty().to_string(), "\ {\n \ \"b\": [\n \ @@ -2678,48 +2725,29 @@ mod tests { // We can't compare the strings directly because the object fields be // printed in a different order. - assert_eq!(a.clone(), from_str(a.to_string().as_slice()).unwrap()); - assert_eq!(a.clone(), - from_str(a.to_pretty_str().as_slice()).unwrap()); - } - - fn with_str_writer(f: F) -> string::String where F: FnOnce(&mut io::Writer){ - let mut m = Vec::new(); - f(&mut m as &mut io::Writer); - string::String::from_utf8(m).unwrap() + assert_eq!(a.clone(), a.to_string().parse().unwrap()); + assert_eq!(a.clone(), a.pretty().to_string().parse().unwrap()); } #[test] fn test_write_enum() { let animal = Dog; assert_eq!( - with_str_writer(|writer| { - let mut encoder = Encoder::new(writer); - animal.encode(&mut encoder).unwrap(); - }), + format!("{}", super::as_json(&animal)), "\"Dog\"" ); assert_eq!( - with_str_writer(|writer| { - let mut encoder = PrettyEncoder::new(writer); - animal.encode(&mut encoder).unwrap(); - }), + format!("{}", super::as_pretty_json(&animal)), "\"Dog\"" ); let animal = Frog("Henry".to_string(), 349); assert_eq!( - with_str_writer(|writer| { - let mut encoder = Encoder::new(writer); - animal.encode(&mut encoder).unwrap(); - }), + format!("{}", super::as_json(&animal)), "{\"variant\":\"Frog\",\"fields\":[\"Henry\",349]}" ); assert_eq!( - with_str_writer(|writer| { - let mut encoder = PrettyEncoder::new(writer); - animal.encode(&mut encoder).unwrap(); - }), + format!("{}", super::as_pretty_json(&animal)), "{\n \ \"variant\": \"Frog\",\n \ \"fields\": [\n \ @@ -2732,16 +2760,10 @@ mod tests { macro_rules! check_encoder_for_simple { ($value:expr, $expected:expr) => ({ - let s = with_str_writer(|writer| { - let mut encoder = Encoder::new(writer); - $value.encode(&mut encoder).unwrap(); - }); + let s = format!("{}", super::as_json(&$value)); assert_eq!(s, $expected); - let s = with_str_writer(|writer| { - let mut encoder = PrettyEncoder::new(writer); - $value.encode(&mut encoder).unwrap(); - }); + let s = format!("{}", super::as_pretty_json(&$value)); assert_eq!(s, $expected); }) } @@ -3320,10 +3342,7 @@ mod tests { let mut hm: HashMap = HashMap::new(); hm.insert(1, true); let mut mem_buf = Vec::new(); - { - let mut encoder = Encoder::new(&mut mem_buf as &mut io::Writer); - hm.encode(&mut encoder).unwrap(); - } + write!(&mut mem_buf, "{}", super::as_pretty_json(&hm)).unwrap(); let json_str = from_utf8(mem_buf[]).unwrap(); match from_str(json_str) { Err(_) => panic!("Unable to parse json_str: {}", json_str), @@ -3339,10 +3358,7 @@ mod tests { let mut hm: HashMap = HashMap::new(); hm.insert(1, true); let mut mem_buf = Vec::new(); - { - let mut encoder = PrettyEncoder::new(&mut mem_buf as &mut io::Writer); - hm.encode(&mut encoder).unwrap() - } + write!(&mut mem_buf, "{}", super::as_pretty_json(&hm)).unwrap(); let json_str = from_utf8(mem_buf[]).unwrap(); match from_str(json_str) { Err(_) => panic!("Unable to parse json_str: {}", json_str), @@ -3373,18 +3389,15 @@ mod tests { // Helper function for counting indents fn indents(source: &str) -> uint { - let trimmed = source.trim_left_chars(' '); + let trimmed = source.trim_left_matches(' '); source.len() - trimmed.len() } // Test up to 4 spaces of indents (more?) for i in range(0, 4u) { let mut writer = Vec::new(); - { - let ref mut encoder = PrettyEncoder::new(&mut writer); - encoder.set_indent(i); - json.encode(encoder).unwrap(); - } + write!(&mut writer, "{}", + super::as_pretty_json(&json).indent(i)).unwrap(); let printed = from_utf8(writer[]).unwrap(); diff --git a/src/libstd/failure.rs b/src/libstd/failure.rs index 7010eae6dba..30dc48f6fcc 100644 --- a/src/libstd/failure.rs +++ b/src/libstd/failure.rs @@ -14,7 +14,6 @@ use prelude::*; use any::{Any, AnyRefExt}; use cell::RefCell; -use fmt; use io::IoResult; use rt::{backtrace, unwind}; use rt::util::{Stderr, Stdio}; @@ -29,10 +28,7 @@ thread_local! { impl Writer for Stdio { fn write(&mut self, bytes: &[u8]) -> IoResult<()> { - fn fmt_write(f: &mut F, bytes: &[u8]) { - let _ = f.write(bytes); - } - fmt_write(self, bytes); + let _ = self.write_bytes(bytes); Ok(()) } } diff --git a/src/libstd/fmt.rs b/src/libstd/fmt.rs index 957dd54a037..32f5f2d4536 100644 --- a/src/libstd/fmt.rs +++ b/src/libstd/fmt.rs @@ -201,7 +201,7 @@ //! // for details, and the function `pad` can be used to pad strings. //! let decimals = f.precision().unwrap_or(3); //! let string = f64::to_str_exact(magnitude, decimals); -//! f.pad_integral(true, "", string.as_bytes()) +//! f.pad_integral(true, "", string.as_slice()) //! } //! } //! @@ -390,13 +390,9 @@ #![experimental] -use io::Writer; -use io; -use result::Result::{Ok, Err}; use string; -use vec::Vec; -pub use core::fmt::{Formatter, Result, FormatWriter, rt}; +pub use core::fmt::{Formatter, Result, Writer, rt}; pub use core::fmt::{Show, Octal, Binary}; pub use core::fmt::{LowerHex, UpperHex, Pointer}; pub use core::fmt::{LowerExp, UpperExp}; @@ -424,16 +420,7 @@ pub use core::fmt::{argument, argumentuint}; #[experimental = "this is an implementation detail of format! and should not \ be called directly"] pub fn format(args: Arguments) -> string::String { - let mut output = Vec::new(); - let _ = write!(&mut output as &mut Writer, "{}", args); - string::String::from_utf8(output).unwrap() -} - -impl<'a> Writer for Formatter<'a> { - fn write(&mut self, b: &[u8]) -> io::IoResult<()> { - match (*self).write(b) { - Ok(()) => Ok(()), - Err(Error) => Err(io::standard_error(io::OtherIoError)) - } - } + let mut output = string::String::new(); + let _ = write!(&mut output, "{}", args); + output } diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index e8b852ee492..3f6eb217245 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -1028,16 +1028,16 @@ pub trait Writer { /// /// This function will return any I/O error reported while formatting. fn write_fmt(&mut self, fmt: fmt::Arguments) -> IoResult<()> { - // Create a shim which translates a Writer to a FormatWriter and saves + // Create a shim which translates a Writer to a fmt::Writer and saves // off I/O errors. instead of discarding them struct Adaptor<'a, T:'a> { inner: &'a mut T, error: IoResult<()>, } - impl<'a, T: Writer> fmt::FormatWriter for Adaptor<'a, T> { - fn write(&mut self, bytes: &[u8]) -> fmt::Result { - match self.inner.write(bytes) { + impl<'a, T: Writer> fmt::Writer for Adaptor<'a, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write(s.as_bytes()) { Ok(()) => Ok(()), Err(e) => { self.error = Err(e); diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs index e0c512706e6..8d7713d0558 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind.rs @@ -493,27 +493,16 @@ pub extern fn rust_begin_unwind(msg: fmt::Arguments, /// the actual formatting into this shared place. #[inline(never)] #[cold] pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, uint)) -> ! { - use fmt::FormatWriter; + use fmt::Writer; // We do two allocations here, unfortunately. But (a) they're // required with the current scheme, and (b) we don't handle // panic + OOM properly anyway (see comment in begin_unwind // below). - struct VecWriter<'a> { v: &'a mut Vec } - - impl<'a> fmt::FormatWriter for VecWriter<'a> { - fn write(&mut self, buf: &[u8]) -> fmt::Result { - self.v.push_all(buf); - Ok(()) - } - } - - let mut v = Vec::new(); - let _ = write!(&mut VecWriter { v: &mut v }, "{}", msg); - - let msg = box String::from_utf8_lossy(v.as_slice()).into_owned(); - begin_unwind_inner(msg, file_line) + let mut s = String::new(); + let _ = write!(&mut s, "{}", msg); + begin_unwind_inner(box s, file_line) } /// This is the entry point of unwinding for panic!() and assert!(). diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index fee86e33455..fa7c305d69e 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -96,8 +96,8 @@ pub const Stdout: Stdio = Stdio(libc::STDOUT_FILENO); #[allow(non_upper_case_globals)] pub const Stderr: Stdio = Stdio(libc::STDERR_FILENO); -impl fmt::FormatWriter for Stdio { - fn write(&mut self, data: &[u8]) -> fmt::Result { +impl Stdio { + pub fn write_bytes(&mut self, data: &[u8]) { #[cfg(unix)] type WriteLen = libc::size_t; #[cfg(windows)] @@ -108,6 +108,12 @@ impl fmt::FormatWriter for Stdio { data.as_ptr() as *const libc::c_void, data.len() as WriteLen); } + } +} + +impl fmt::Writer for Stdio { + fn write_str(&mut self, data: &str) -> fmt::Result { + self.write_bytes(data.as_bytes()); Ok(()) // yes, we're lying } } @@ -117,16 +123,16 @@ pub fn dumb_print(args: fmt::Arguments) { } pub fn abort(args: fmt::Arguments) -> ! { - use fmt::FormatWriter; + use fmt::Writer; struct BufWriter<'a> { buf: &'a mut [u8], pos: uint, } - impl<'a> FormatWriter for BufWriter<'a> { - fn write(&mut self, bytes: &[u8]) -> fmt::Result { + impl<'a> fmt::Writer for BufWriter<'a> { + fn write_str(&mut self, bytes: &str) -> fmt::Result { let left = self.buf.slice_from_mut(self.pos); - let to_write = bytes[..cmp::min(bytes.len(), left.len())]; + let to_write = bytes.as_bytes()[..cmp::min(bytes.len(), left.len())]; slice::bytes::copy_memory(left, to_write); self.pos += to_write.len(); Ok(()) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 12432c8c78f..4606e70d83b 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1662,6 +1662,7 @@ mod test { use serialize; use codemap::*; use super::*; + use std::fmt; // are ASTs encodable? #[test] @@ -1687,6 +1688,6 @@ mod test { exported_macros: Vec::new(), }; // doesn't matter which encoder we use.... - let _f = &e as &serialize::Encodable; + let _f = &e as &serialize::Encodable; } } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 19821ecb7ca..565c98797b8 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -171,14 +171,14 @@ impl TestFn { impl fmt::Show for TestFn { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write(match *self { + f.write_str(match *self { StaticTestFn(..) => "StaticTestFn(..)", StaticBenchFn(..) => "StaticBenchFn(..)", StaticMetricFn(..) => "StaticMetricFn(..)", DynTestFn(..) => "DynTestFn(..)", DynMetricFn(..) => "DynMetricFn(..)", DynBenchFn(..) => "DynBenchFn(..)" - }.as_bytes()) + }) } } @@ -1212,8 +1212,7 @@ impl MetricMap { pub fn save(&self, p: &Path) -> io::IoResult<()> { let mut file = try!(File::create(p)); let MetricMap(ref map) = *self; - let mut enc = json::PrettyEncoder::new(&mut file); - map.encode(&mut enc) + write!(&mut file, "{}", json::as_json(map)) } /// Compare against another MetricMap. Optionally compare all diff --git a/src/test/compile-fail/variance-trait-matching-2.rs b/src/test/compile-fail/variance-trait-matching-2.rs index ed4fdc52572..cae7a4cefad 100644 --- a/src/test/compile-fail/variance-trait-matching-2.rs +++ b/src/test/compile-fail/variance-trait-matching-2.rs @@ -10,17 +10,17 @@ extern crate serialize; -use std::io; +use std::fmt; use serialize::{Encodable, Encoder}; pub fn buffer_encode<'a, - T:Encodable,io::IoError>>( + T:Encodable,fmt::Error>>( to_encode_object: &T) - -> Vec { - let mut m = Vec::new(); + -> String { + let mut m = String::new(); { let mut encoder = - serialize::json::Encoder::new(&mut m as &mut io::Writer); + serialize::json::Encoder::new(&mut m); //~^ ERROR `m` does not live long enough to_encode_object.encode(&mut encoder); } diff --git a/src/test/run-fail/panic-non-utf8.rs b/src/test/run-fail/panic-non-utf8.rs deleted file mode 100644 index 8b013199369..00000000000 --- a/src/test/run-fail/panic-non-utf8.rs +++ /dev/null @@ -1,26 +0,0 @@ - -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Previously failed formating invalid utf8. -// cc #16877 - -// error-pattern:panicked at 'hello�' - -struct Foo; -impl std::fmt::Show for Foo { - fn fmt(&self, fmtr:&mut std::fmt::Formatter) -> std::fmt::Result { - // Purge invalid utf8: 0xff - fmtr.write(&[104, 101, 108, 108, 111, 0xff]) - } -} -fn main() { - panic!("{}", Foo) -} diff --git a/src/test/run-pass/colorful-write-macros.rs b/src/test/run-pass/colorful-write-macros.rs index bbb049eb960..d2caecdf05b 100644 --- a/src/test/run-pass/colorful-write-macros.rs +++ b/src/test/run-pass/colorful-write-macros.rs @@ -15,7 +15,6 @@ use std::io::MemWriter; use std::fmt; -use std::fmt::FormatWriter; struct Foo<'a> { writer: &'a mut (Writer+'a), @@ -24,8 +23,8 @@ struct Foo<'a> { struct Bar; -impl fmt::FormatWriter for Bar { - fn write(&mut self, _: &[u8]) -> fmt::Result { +impl fmt::Writer for Bar { + fn write_str(&mut self, _: &str) -> fmt::Result { Ok(()) } } @@ -41,5 +40,8 @@ fn main() { println!("ok"); let mut s = Bar; - write!(&mut s, "test"); + { + use std::fmt::Writer; + write!(&mut s, "test"); + } } diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index fa62699a303..1efae89f665 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -16,7 +16,6 @@ #![allow(unused_must_use)] use std::fmt; -use std::io; struct A; struct B; @@ -24,17 +23,17 @@ struct C; impl fmt::LowerHex for A { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write("aloha".as_bytes()) + f.write_str("aloha") } } impl fmt::UpperHex for B { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write("adios".as_bytes()) + f.write_str("adios") } } impl fmt::Show for C { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad_integral(true, "☃", "123".as_bytes()) + f.pad_integral(true, "☃", "123") } } @@ -160,18 +159,18 @@ pub fn main() { // Basic test to make sure that we can invoke the `write!` macro with an // io::Writer instance. fn test_write() { - let mut buf = Vec::new(); - write!(&mut buf as &mut io::Writer, "{}", 3i); + use std::fmt::Writer; + let mut buf = String::new(); + write!(&mut buf, "{}", 3i); { - let w = &mut buf as &mut io::Writer; + let w = &mut buf; write!(w, "{foo}", foo=4i); write!(w, "{}", "hello"); writeln!(w, "{}", "line"); writeln!(w, "{foo}", foo="bar"); } - let s = String::from_utf8(buf).unwrap(); - t!(s, "34helloline\nbar\n"); + t!(buf, "34helloline\nbar\n"); } // Just make sure that the macros are defined, there's not really a lot that we @@ -187,14 +186,15 @@ fn test_print() { // Just make sure that the macros are defined, there's not really a lot that we // can do with them just yet (to test the output) fn test_format_args() { - let mut buf = Vec::new(); + use std::fmt::Writer; + let mut buf = String::new(); { - let w = &mut buf as &mut io::Writer; + let w = &mut buf; write!(w, "{}", format_args!("{}", 1i)); write!(w, "{}", format_args!("test")); write!(w, "{}", format_args!("{test}", test=3i)); } - let s = String::from_utf8(buf).unwrap(); + let s = buf; t!(s, "1test3"); let s = fmt::format(format_args!("hello {}", "world")); diff --git a/src/test/run-pass/issue-11881.rs b/src/test/run-pass/issue-11881.rs index 8de847bfa33..0e0aea081f6 100644 --- a/src/test/run-pass/issue-11881.rs +++ b/src/test/run-pass/issue-11881.rs @@ -12,7 +12,8 @@ extern crate rbml; extern crate serialize; use std::io; -use std::io::{IoError, IoResult, SeekStyle}; +use std::fmt; +use std::io::{IoResult, SeekStyle}; use std::slice; use serialize::{Encodable, Encoder}; @@ -37,16 +38,15 @@ enum WireProtocol { // ... } -fn encode_json<'a, - T: Encodable, - std::io::IoError>>(val: &T, - wr: &'a mut SeekableMemWriter) { - let mut encoder = json::Encoder::new(wr); - val.encode(&mut encoder); +fn encode_json< + T: for<'a> Encodable, + fmt::Error>>(val: &T, + wr: &mut SeekableMemWriter) { + write!(wr, "{}", json::as_json(val)); } fn encode_rbml<'a, T: Encodable, - std::io::IoError>>(val: &T, + io::IoError>>(val: &T, wr: &'a mut SeekableMemWriter) { let mut encoder = writer::Encoder::new(wr); val.encode(&mut encoder); diff --git a/src/test/run-pass/issue-15924.rs b/src/test/run-pass/issue-15924.rs index 8d5b928964d..1ab8deda383 100644 --- a/src/test/run-pass/issue-15924.rs +++ b/src/test/run-pass/issue-15924.rs @@ -12,7 +12,7 @@ extern crate serialize; -use std::io::IoError; +use std::fmt; use serialize::{Encoder, Encodable}; use serialize::json; @@ -21,7 +21,7 @@ struct Foo { } #[unsafe_destructor] -impl<'a, T: Encodable, IoError>> Drop for Foo { +impl Encodable, fmt::Error>> Drop for Foo { fn drop(&mut self) { json::encode(&self.v); }