diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2013-08-10 00:28:47 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2013-08-12 23:18:51 -0700 |
| commit | b820748ff5a2bc09b58dd7ac511e0f607d55f2e9 (patch) | |
| tree | 7a34e364ae23968eadba2e7bc0ecae8e20b3fced /src/libstd/fmt | |
| parent | 44675ac6aff91889f960655b0034964740415e8c (diff) | |
| download | rust-b820748ff5a2bc09b58dd7ac511e0f607d55f2e9.tar.gz rust-b820748ff5a2bc09b58dd7ac511e0f607d55f2e9.zip | |
Implement formatting arguments for strings and integers
Closes #1651
Diffstat (limited to 'src/libstd/fmt')
| -rw-r--r-- | src/libstd/fmt/mod.rs | 167 |
1 files changed, 145 insertions, 22 deletions
diff --git a/src/libstd/fmt/mod.rs b/src/libstd/fmt/mod.rs index 2b8807b2291..a25620cfa69 100644 --- a/src/libstd/fmt/mod.rs +++ b/src/libstd/fmt/mod.rs @@ -11,7 +11,7 @@ use prelude::*; use cast; -use int; +use char::Char; use rt::io::Decorator; use rt::io::mem::MemWriter; use rt::io; @@ -122,6 +122,11 @@ pub unsafe fn sprintf(fmt: &[rt::Piece], args: &[Argument]) -> ~str { } impl<'self> Formatter<'self> { + + // First up is the collection of functions used to execute a format string + // at runtime. This consumes all of the compile-time statics generated by + // the ifmt! syntax extension. + fn run(&mut self, piece: &rt::Piece, cur: Option<&str>) { let setcount = |slot: &mut Option<uint>, cnt: &parse::Count| { match *cnt { @@ -240,6 +245,118 @@ impl<'self> Formatter<'self> { } } } + + // Helper methods used for padding and processing formatting arguments that + // all formatting traits can use. + + /// TODO: dox + pub fn pad_integral(&mut self, s: &[u8], alternate_prefix: &str, + positive: bool) { + use fmt::parse::{FlagAlternate, FlagSignPlus}; + + let mut actual_len = s.len(); + if self.flags & 1 << (FlagAlternate as uint) != 0 { + actual_len += alternate_prefix.len(); + } + if self.flags & 1 << (FlagSignPlus as uint) != 0 { + actual_len += 1; + } + if !positive { + actual_len += 1; + } + + let emit = |this: &mut Formatter| { + if this.flags & 1 << (FlagSignPlus as uint) != 0 && positive { + this.buf.write(['+' as u8]); + } else if !positive { + this.buf.write(['-' as u8]); + } + if this.flags & 1 << (FlagAlternate as uint) != 0 { + this.buf.write(alternate_prefix.as_bytes()); + } + this.buf.write(s); + }; + + match self.width { + None => { emit(self) } + Some(min) if actual_len >= min => { emit(self) } + Some(min) => { + do self.with_padding(min - actual_len) |me| { + emit(me); + } + } + } + } + + /// This function takes a string slice and emits it to the internal buffer + /// after applying the relevant formatting flags specified. The flags + /// recognized for generic strings are: + /// + /// * width - the minimum width of what to emit + /// * fill/alignleft - what to emit and where to emit it if the string + /// provided needs to be padded + /// * precision - the maximum length to emit, the string is truncated if it + /// is longer than this length + /// + /// Notably this function ignored the `flag` parameters + pub fn pad(&mut self, s: &str) { + // Make sure there's a fast path up front + if self.width.is_none() && self.precision.is_none() { + self.buf.write(s.as_bytes()); + return + } + // The `precision` field can be interpreted as a `max-width` for the + // string being formatted + match self.precision { + Some(max) => { + // If there's a maximum width and our string is longer than + // that, then we must always have truncation. This is the only + // case where the maximum length will matter. + let char_len = s.char_len(); + if char_len >= max { + let nchars = uint::min(max, char_len); + self.buf.write(s.slice_chars(0, nchars).as_bytes()); + return + } + } + None => {} + } + + // The `width` field is more of a `min-width` parameter at this point. + 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()) } + + // 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()) + } + + // 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) => { + do self.with_padding(width - s.len()) |me| { + me.buf.write(s.as_bytes()); + } + } + } + } + + fn with_padding(&mut self, padding: uint, f: &fn(&mut Formatter)) { + if self.alignleft { + f(self); + } + let mut fill = [0u8, ..4]; + let len = self.fill.encode_utf8(fill); + for _ in range(0, padding) { + self.buf.write(fill.slice_to(len)); + } + if !self.alignleft { + f(self); + } + } } /// This is a function which calls are emitted to by the compiler itself to @@ -279,60 +396,53 @@ impl Bool for bool { impl<'self> String for &'self str { fn fmt(s: & &'self str, f: &mut Formatter) { - // XXX: formatting args - f.buf.write(s.as_bytes()) + f.pad(*s); } } impl Char for char { fn fmt(c: &char, f: &mut Formatter) { - // XXX: formatting args - // XXX: shouldn't require an allocation - let mut s = ~""; - s.push_char(*c); - f.buf.write(s.as_bytes()); + let mut utf8 = [0u8, ..4]; + let amt = c.encode_utf8(utf8); + let s: &str = unsafe { cast::transmute(utf8.slice_to(amt)) }; + String::fmt(&s, f); } } impl Signed for int { fn fmt(c: &int, f: &mut Formatter) { - // XXX: formatting args - do int::to_str_bytes(*c, 10) |buf| { - f.buf.write(buf); + do uint::to_str_bytes(c.abs() as uint, 10) |buf| { + f.pad_integral(buf, "", *c >= 0); } } } impl Unsigned for uint { fn fmt(c: &uint, f: &mut Formatter) { - // XXX: formatting args do uint::to_str_bytes(*c, 10) |buf| { - f.buf.write(buf); + f.pad_integral(buf, "", true); } } } impl Octal for uint { fn fmt(c: &uint, f: &mut Formatter) { - // XXX: formatting args do uint::to_str_bytes(*c, 8) |buf| { - f.buf.write(buf); + f.pad_integral(buf, "0o", true); } } } impl LowerHex for uint { fn fmt(c: &uint, f: &mut Formatter) { - // XXX: formatting args do uint::to_str_bytes(*c, 16) |buf| { - f.buf.write(buf); + f.pad_integral(buf, "0x", true); } } } impl UpperHex for uint { fn fmt(c: &uint, f: &mut Formatter) { - // XXX: formatting args do uint::to_str_bytes(*c, 16) |buf| { let mut local = [0u8, ..16]; for (l, &b) in local.mut_iter().zip(buf.iter()) { @@ -341,16 +451,29 @@ impl UpperHex for uint { _ => b, }; } - f.buf.write(local.slice_to(buf.len())); + f.pad_integral(local.slice_to(buf.len()), "0x", true); } } } impl<T> Poly for T { fn fmt(t: &T, f: &mut Formatter) { - // XXX: formatting args - let s = sys::log_str(t); - f.buf.write(s.as_bytes()); + match (f.width, f.precision) { + (None, None) => { + // XXX: sys::log_str should have a variant which takes a stream + // and we should directly call that (avoids unnecessary + // allocations) + let s = sys::log_str(t); + f.buf.write(s.as_bytes()); + } + + // If we have a specified width for formatting, then we have to make + // this allocation of a new string + _ => { + let s = sys::log_str(t); + f.pad(s); + } + } } } |
