extfmt.rs 20.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2012 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

J
Jesse Jones 已提交
11 12 13
//! Support for fmt! expressions.
//!
//! The syntax is close to that of Posix format strings:
J
Jesse Jones 已提交
14
//!
J
Jesse Jones 已提交
15 16 17 18 19 20 21 22
//! ~~~~~~
//! Format := '%' Parameter? Flag* Width? Precision? Type
//! Parameter := [0-9]+ '$'
//! Flag := [ 0#+-]
//! Width := Parameter | [0-9]+
//! Precision := '.' [0-9]+
//! Type := [bcdfiostuxX?]
//! ~~~~~~
J
Jesse Jones 已提交
23 24 25 26 27 28 29 30 31
//!
//! * Parameter is the 1-based argument to apply the format to. Currently not
//! implemented.
//! * Flag 0 causes leading zeros to be used for padding when converting
//! numbers.
//! * Flag # causes the conversion to be done in an *alternative* manner.
//! Currently not implemented.
//! * Flag + causes signed numbers to always be prepended with a sign
//! character.
J
Jesse Jones 已提交
32
//! * Flag - left justifies the result
J
Jesse Jones 已提交
33 34 35 36
//! * Width specifies the minimum field width of the result. By default
//! leading spaces are added.
//! * Precision specifies the minimum number of digits for integral types
//! and the minimum number
J
Jesse Jones 已提交
37
//! of decimal places for float.
J
Jesse Jones 已提交
38
//!
J
Jesse Jones 已提交
39
//! The types currently supported are:
J
Jesse Jones 已提交
40
//!
J
Jesse Jones 已提交
41 42 43 44 45 46 47 48 49 50 51 52 53
//! * b - bool
//! * c - char
//! * d - int
//! * f - float
//! * i - int (same as d)
//! * o - uint as octal
//! * t - uint as binary
//! * u - uint
//! * x - uint as lower-case hexadecimal
//! * X - uint as upper-case hexadecimal
//! * s - str (any flavor)
//! * ? - arbitrary type (does not use the to_str trait)

54 55 56 57 58 59 60 61 62
/*
Syntax Extension: fmt

Format a string

The 'fmt' extension is modeled on the posix printf system.

A posix conversion ostensibly looks like this

63
> %~[parameter]~[flags]~[width]~[.precision]~[length]type
64 65 66 67

Given the different numeric type bestiary we have, we omit the 'length'
parameter and support slightly different conversions for 'type'

68
> %~[parameter]~[flags]~[width]~[.precision]type
69 70 71 72 73 74

we also only support translating-to-rust a tiny subset of the possible
combinations at the moment.

Example:

P
Paul Stansifer 已提交
75
debug!("hello, %s!", "world");
76 77 78

*/

79
use prelude::*;
80 81 82 83 84 85 86 87 88 89 90 91 92

/*
 * We have a 'ct' (compile-time) module that parses format strings into a
 * sequence of conversions. From those conversions AST fragments are built
 * that call into properly-typed functions in the 'rt' (run-time) module.
 * Each of those run-time conversion functions accepts another conversion
 * description that specifies how to format its output.
 *
 * The building of the AST is currently done in a module inside the compiler,
 * but should migrate over here as the plugin interface is defined.
 */

// Functions used by the fmt extension at compile time
J
Jesse Jones 已提交
93
#[doc(hidden)]
G
Graydon Hoare 已提交
94
pub mod ct {
95
    use char;
96
    use container::Container;
97
    use prelude::*;
98 99
    use str;

100
    #[deriving(Eq)]
G
Graydon Hoare 已提交
101
    pub enum Signedness { Signed, Unsigned, }
102

103
    #[deriving(Eq)]
G
Graydon Hoare 已提交
104
    pub enum Caseness { CaseUpper, CaseLower, }
105

106
    #[deriving(Eq)]
G
Graydon Hoare 已提交
107
    pub enum Ty {
108 109 110 111 112 113 114 115
        TyBool,
        TyStr,
        TyChar,
        TyInt(Signedness),
        TyBits,
        TyHex(Caseness),
        TyOctal,
        TyFloat,
116
        TyPointer,
117
        TyPoly,
118
    }
119

120
    #[deriving(Eq)]
G
Graydon Hoare 已提交
121
    pub enum Flag {
122 123 124 125 126
        FlagLeftJustify,
        FlagLeftZeroPad,
        FlagSpaceForSign,
        FlagSignAlways,
        FlagAlternate,
127
    }
128

129
    #[deriving(Eq)]
T
Tim Chevalier 已提交
130 131 132 133 134 135
    pub enum Count {
        CountIs(uint),
        CountIsParam(uint),
        CountIsNextParam,
        CountImplied,
    }
136

137
    #[deriving(Eq)]
138 139 140 141 142
    struct Parsed<T> {
        val: T,
        next: uint
    }

143 144
    impl<T> Parsed<T> {
        pub fn new(val: T, next: uint) -> Parsed<T> {
145
            Parsed {val: val, next: next}
146 147 148
        }
    }

T
Tim Chevalier 已提交
149
    // A formatted conversion from an expression to a string
150
    #[deriving(Eq)]
151 152 153 154 155 156 157
    pub struct Conv {
        param: Option<uint>,
        flags: ~[Flag],
        width: Count,
        precision: Count,
        ty: Ty
    }
158 159

    // A fragment of the output sequence
160
    #[deriving(Eq)]
G
Graydon Hoare 已提交
161
    pub enum Piece { PieceString(~str), PieceConv(Conv), }
162 163

    pub type ErrorFn = @fn(&str) -> !;
164

165
    pub fn parse_fmt_string(s: &str, err: ErrorFn) -> ~[Piece] {
166 167
        fn push_slice(ps: &mut ~[Piece], s: &str, from: uint, to: uint) {
            if to > from {
168
                ps.push(PieceString(s.slice(from, to).to_owned()));
169 170
            }
        }
171 172 173

        let lim = s.len();
        let mut h = 0;
174
        let mut i = 0;
175 176
        let mut pieces = ~[];

177
        while i < lim {
178
            if s[i] == '%' as u8 {
179
                i += 1;
180

181
                if i >= lim {
182
                    err("unterminated conversion at end of string");
183 184
                } else if s[i] == '%' as u8 {
                    push_slice(&mut pieces, s, h, i);
185
                    i += 1;
186
                } else {
187 188 189 190
                    push_slice(&mut pieces, s, h, i - 1);
                    let Parsed {val, next} = parse_conversion(s, i, lim, err);
                    pieces.push(val);
                    i = next;
191
                }
192 193 194 195 196

                h = i;
            } else {
                i += str::utf8_char_width(s[i]);
            }
197
        }
198 199 200

        push_slice(&mut pieces, s, h, i);
        pieces
201
    }
202 203 204 205

    pub fn peek_num(s: &str, i: uint, lim: uint) -> Option<Parsed<uint>> {
        let mut i = i;
        let mut accum = 0;
206
        let mut found = false;
207 208 209

        while i < lim {
            match char::to_digit(s[i] as char, 10) {
B
Brian Anderson 已提交
210
                Some(x) => {
211 212 213
                    found = true;
                    accum *= 10;
                    accum += x;
214 215
                    i += 1;
                }
B
Brian Anderson 已提交
216
                None => break
217 218
            }
        }
219

220
        if found {
221
            Some(Parsed::new(accum, i))
222
        } else {
B
Brian Anderson 已提交
223
            None
224
        }
225
    }
226 227 228 229

    pub fn parse_conversion(s: &str, i: uint, lim: uint, err: ErrorFn) ->
        Parsed<Piece> {
        let param = parse_parameter(s, i, lim);
230 231
        // avoid copying ~[Flag] by destructuring
        let Parsed {val: flags_val, next: flags_next} = parse_flags(s,
232
            param.next, lim);
233
        let width = parse_count(s, flags_next, lim);
234
        let prec = parse_precision(s, width.next, lim);
235
        let ty = parse_type(s, prec.next, lim, err);
236 237 238 239 240 241 242

        Parsed::new(PieceConv(Conv {
            param: param.val,
            flags: flags_val,
            width: width.val,
            precision: prec.val,
            ty: ty.val}), ty.next)
243
    }
244

T
Tim Chevalier 已提交
245
    pub fn parse_parameter(s: &str, i: uint, lim: uint) ->
246
        Parsed<Option<uint>> {
247
        if i >= lim { return Parsed::new(None, i); }
248

249 250 251 252 253
        match peek_num(s, i, lim) {
            Some(num) if num.next < lim && s[num.next] == '$' as u8 =>
                Parsed::new(Some(num.val), num.next + 1),
            _ => Parsed::new(None, i)
        }
254
    }
255 256

    pub fn parse_flags(s: &str, i: uint, lim: uint) -> Parsed<~[Flag]> {
A
Andrew Paseltiner 已提交
257 258
        let mut i = i;
        let mut flags = ~[];
259

A
Andrew Paseltiner 已提交
260
        while i < lim {
261 262 263 264 265 266
            let f = match s[i] as char {
                '-' => FlagLeftJustify,
                '0' => FlagLeftZeroPad,
                ' ' => FlagSpaceForSign,
                '+' => FlagSignAlways,
                '#' => FlagAlternate,
A
Andrew Paseltiner 已提交
267 268 269 270 271
                _ => break
            };

            flags.push(f);
            i += 1;
272
        }
A
Andrew Paseltiner 已提交
273 274

        Parsed::new(flags, i)
275
    }
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291

    pub fn parse_count(s: &str, i: uint, lim: uint) -> Parsed<Count> {
        if i >= lim {
            Parsed::new(CountImplied, i)
        } else if s[i] == '*' as u8 {
            let param = parse_parameter(s, i + 1, lim);
            let j = param.next;

            match param.val {
                None => Parsed::new(CountIsNextParam, j),
                Some(n) => Parsed::new(CountIsParam(n), j)
            }
        } else {
            match peek_num(s, i, lim) {
                None => Parsed::new(CountImplied, i),
                Some(num) => Parsed::new(CountIs(num.val), num.next)
A
Andrew Paseltiner 已提交
292
            }
293
        }
T
Tim Chevalier 已提交
294
    }
295

296 297 298
    pub fn parse_precision(s: &str, i: uint, lim: uint) -> Parsed<Count> {
        if i < lim && s[i] == '.' as u8 {
            let count = parse_count(s, i + 1, lim);
299

300 301 302 303 304 305 306 307 308
            // If there were no digits specified, i.e. the precision
            // was ".", then the precision is 0
            match count.val {
                CountImplied => Parsed::new(CountIs(0), count.next),
                _ => count
            }
        } else {
            Parsed::new(CountImplied, i)
        }
309
    }
310

311
    pub fn parse_type(s: &str, i: uint, lim: uint, err: ErrorFn) ->
312
        Parsed<Ty> {
313
        if i >= lim { err("missing type in conversion"); }
314

315
        // FIXME (#2249): Do we really want two signed types here?
316
        // How important is it to be printf compatible?
317 318 319 320 321 322 323 324 325 326 327
        let t = match s[i] as char {
            'b' => TyBool,
            's' => TyStr,
            'c' => TyChar,
            'd' | 'i' => TyInt(Signed),
            'u' => TyInt(Unsigned),
            'x' => TyHex(CaseLower),
            'X' => TyHex(CaseUpper),
            't' => TyBits,
            'o' => TyOctal,
            'f' => TyFloat,
328
            'p' => TyPointer,
329
            '?' => TyPoly,
330
            _ => err(fmt!("unknown type in conversion: %c", s.char_at(i)))
A
Andrew Paseltiner 已提交
331
        };
332

A
Andrew Paseltiner 已提交
333
        Parsed::new(t, i + 1)
334
    }
A
Andrew Paseltiner 已提交
335 336

    #[cfg(test)]
337
    fn die(s: &str) -> ! { fail!(s.to_owned()) }
A
Andrew Paseltiner 已提交
338 339 340 341 342 343 344

    #[test]
    fn test_parse_count() {
        fn test(s: &str, count: Count, next: uint) -> bool {
            parse_count(s, 0, s.len()) == Parsed::new(count, next)
        }

P
Patrick Walton 已提交
345 346 347 348 349
        assert!(test("", CountImplied, 0));
        assert!(test("*", CountIsNextParam, 1));
        assert!(test("*1", CountIsNextParam, 1));
        assert!(test("*1$", CountIsParam(1), 3));
        assert!(test("123", CountIs(123), 3));
A
Andrew Paseltiner 已提交
350 351 352 353 354
    }

    #[test]
    fn test_parse_flags() {
        fn pack(fs: &[Flag]) -> uint {
355
            fs.iter().fold(0, |p, &f| p | (1 << f as uint))
A
Andrew Paseltiner 已提交
356 357 358 359
        }

        fn test(s: &str, flags: &[Flag], next: uint) {
            let f = parse_flags(s, 0, s.len());
360 361
            assert_eq!(pack(f.val), pack(flags));
            assert_eq!(f.next, next);
A
Andrew Paseltiner 已提交
362 363 364 365 366 367 368 369 370 371
        }

        test("", [], 0);
        test("!#-+ 0", [], 0);
        test("#-+", [FlagAlternate, FlagLeftJustify, FlagSignAlways], 3);
        test(" 0", [FlagSpaceForSign, FlagLeftZeroPad], 2);
    }

    #[test]
    fn test_parse_fmt_string() {
P
Patrick Walton 已提交
372
        assert!(parse_fmt_string("foo %s bar", die) == ~[
A
Andrew Paseltiner 已提交
373
            PieceString(~"foo "),
374 375 376 377 378 379 380
            PieceConv(Conv {
                param: None,
                flags: ~[],
                width: CountImplied,
                precision: CountImplied,
                ty: TyStr,
            }),
381
            PieceString(~" bar")]);
A
Andrew Paseltiner 已提交
382

P
Patrick Walton 已提交
383
        assert!(parse_fmt_string("%s", die) == ~[
384 385 386 387 388 389
            PieceConv(Conv {
                param: None,
                flags: ~[],
                width: CountImplied,
                precision: CountImplied,
                ty: TyStr,
390
            })]);
A
Andrew Paseltiner 已提交
391

P
Patrick Walton 已提交
392
        assert!(parse_fmt_string("%%%%", die) == ~[
393
            PieceString(~"%"), PieceString(~"%")]);
A
Andrew Paseltiner 已提交
394 395 396 397 398 399 400 401
    }

    #[test]
    fn test_parse_parameter() {
        fn test(s: &str, param: Option<uint>, next: uint) -> bool {
            parse_parameter(s, 0, s.len()) == Parsed::new(param, next)
        }

P
Patrick Walton 已提交
402 403 404 405
        assert!(test("", None, 0));
        assert!(test("foo", None, 0));
        assert!(test("123", None, 0));
        assert!(test("123$", Some(123), 4));
A
Andrew Paseltiner 已提交
406 407 408 409 410 411 412 413
    }

    #[test]
    fn test_parse_precision() {
        fn test(s: &str, count: Count, next: uint) -> bool {
            parse_precision(s, 0, s.len()) == Parsed::new(count, next)
        }

P
Patrick Walton 已提交
414 415 416 417 418 419
        assert!(test("", CountImplied, 0));
        assert!(test(".", CountIs(0), 1));
        assert!(test(".*", CountIsNextParam, 2));
        assert!(test(".*1", CountIsNextParam, 2));
        assert!(test(".*1$", CountIsParam(1), 4));
        assert!(test(".123", CountIs(123), 4));
A
Andrew Paseltiner 已提交
420 421 422 423 424 425 426 427
    }

    #[test]
    fn test_parse_type() {
        fn test(s: &str, ty: Ty) -> bool {
            parse_type(s, 0, s.len(), die) == Parsed::new(ty, 1)
        }

P
Patrick Walton 已提交
428 429 430 431 432 433 434 435 436 437
        assert!(test("b", TyBool));
        assert!(test("c", TyChar));
        assert!(test("d", TyInt(Signed)));
        assert!(test("f", TyFloat));
        assert!(test("i", TyInt(Signed)));
        assert!(test("o", TyOctal));
        assert!(test("s", TyStr));
        assert!(test("t", TyBits));
        assert!(test("x", TyHex(CaseLower)));
        assert!(test("X", TyHex(CaseUpper)));
438
        assert!(test("p", TyPointer));
P
Patrick Walton 已提交
439
        assert!(test("?", TyPoly));
A
Andrew Paseltiner 已提交
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
    }

    #[test]
    #[should_fail]
    fn test_parse_type_missing() {
        parse_type("", 0, 0, die);
    }

    #[test]
    #[should_fail]
    fn test_parse_type_unknown() {
        parse_type("!", 0, 1, die);
    }

    #[test]
    fn test_peek_num() {
        let s1 = "";
P
Patrick Walton 已提交
457
        assert!(peek_num(s1, 0, s1.len()).is_none());
A
Andrew Paseltiner 已提交
458 459

        let s2 = "foo";
P
Patrick Walton 已提交
460
        assert!(peek_num(s2, 0, s2.len()).is_none());
A
Andrew Paseltiner 已提交
461 462

        let s3 = "123";
463
        assert_eq!(peek_num(s3, 0, s3.len()), Some(Parsed::new(123, 3)));
A
Andrew Paseltiner 已提交
464 465

        let s4 = "123foo";
466
        assert_eq!(peek_num(s4, 0, s4.len()), Some(Parsed::new(123, 3)));
A
Andrew Paseltiner 已提交
467
    }
468 469
}

470 471 472
// Functions used by the fmt extension at runtime. For now there are a lot of
// decisions made a runtime. If it proves worthwhile then some of these
// conditions can be evaluated at compile-time. For now though it's cleaner to
E
Erick Tryzelaar 已提交
473
// implement it this way, I think.
474
#[doc(hidden)]
475
#[allow(non_uppercase_statics)]
476 477 478 479
pub mod rt {
    use float;
    use str;
    use sys;
480
    use num;
481 482 483
    use vec;
    use option::{Some, None, Option};

484 485 486 487 488 489
    pub static flag_none : u32 = 0u32;
    pub static flag_left_justify   : u32 = 0b00000000000001u32;
    pub static flag_left_zero_pad  : u32 = 0b00000000000010u32;
    pub static flag_space_for_sign : u32 = 0b00000000000100u32;
    pub static flag_sign_always    : u32 = 0b00000000001000u32;
    pub static flag_alternate      : u32 = 0b00000000010000u32;
490 491 492 493 494 495 496 497 498 499 500 501

    pub enum Count { CountIs(uint), CountImplied, }

    pub enum Ty { TyDefault, TyBits, TyHexUpper, TyHexLower, TyOctal, }

    pub struct Conv {
        flags: u32,
        width: Count,
        precision: Count,
        ty: Ty,
    }

502
    pub fn conv_int(cv: Conv, i: int, buf: &mut ~str) {
503 504
        let radix = 10;
        let prec = get_int_precision(cv);
505
        let s : ~str = uint_to_str_prec(num::abs(i) as uint, radix, prec);
506 507 508 509 510 511 512 513 514 515

        let head = if i >= 0 {
            if have_flag(cv.flags, flag_sign_always) {
                Some('+')
            } else if have_flag(cv.flags, flag_space_for_sign) {
                Some(' ')
            } else {
                None
            }
        } else { Some('-') };
516
        pad(cv, s, head, PadSigned, buf);
517
    }
518
    pub fn conv_uint(cv: Conv, u: uint, buf: &mut ~str) {
519
        let prec = get_int_precision(cv);
520
        let rs =
521 522 523
            match cv.ty {
              TyDefault => uint_to_str_prec(u, 10, prec),
              TyHexLower => uint_to_str_prec(u, 16, prec),
524

525
              // FIXME: #4318 Instead of to_ascii and to_str_ascii, could use
526
              // to_ascii_move and to_str_move to not do a unnecessary copy.
527 528 529 530
              TyHexUpper => {
                let s = uint_to_str_prec(u, 16, prec);
                s.to_ascii().to_upper().to_str_ascii()
              }
531 532 533
              TyBits => uint_to_str_prec(u, 2, prec),
              TyOctal => uint_to_str_prec(u, 8, prec)
            };
534
        pad(cv, rs, None, PadUnsigned, buf);
535
    }
536
    pub fn conv_bool(cv: Conv, b: bool, buf: &mut ~str) {
537 538 539 540 541
        let s = if b { "true" } else { "false" };
        // run the boolean conversion through the string conversion logic,
        // giving it the same rules for precision, etc.
        conv_str(cv, s, buf);
    }
542
    pub fn conv_char(cv: Conv, c: char, buf: &mut ~str) {
543
        pad(cv, "", Some(c), PadNozero, buf);
544
    }
545
    pub fn conv_str(cv: Conv, s: &str, buf: &mut ~str) {
546 547
        // For strings, precision is the maximum characters
        // displayed
548
        let unpadded = match cv.precision {
549 550 551 552 553 554 555 556
            CountImplied => s,
            CountIs(max) => {
                if (max as uint) < s.char_len() {
                    s.slice(0, max as uint)
                } else {
                    s
                }
            }
557
        };
558
        pad(cv, unpadded, None, PadNozero, buf);
559
    }
560
    pub fn conv_float(cv: Conv, f: float, buf: &mut ~str) {
561 562 563 564
        let (to_str, digits) = match cv.precision {
              CountIs(c) => (float::to_str_exact, c as uint),
              CountImplied => (float::to_str_digits, 6u)
        };
565
        let s = to_str(f, digits);
566 567 568 569 570 571 572 573 574
        let head = if 0.0 <= f {
            if have_flag(cv.flags, flag_sign_always) {
                Some('+')
            } else if have_flag(cv.flags, flag_space_for_sign) {
                Some(' ')
            } else {
                None
            }
        } else { None };
575
        pad(cv, s, head, PadFloat, buf);
576
    }
577 578 579 580
    pub fn conv_pointer<T>(cv: Conv, ptr: *T, buf: &mut ~str) {
        let s = ~"0x" + uint_to_str_prec(ptr as uint, 16, 1u);
        pad(cv, s, None, PadNozero, buf);
    }
581
    pub fn conv_poly<T>(cv: Conv, v: &T, buf: &mut ~str) {
582 583 584 585 586 587 588
        let s = sys::log_str(v);
        conv_str(cv, s, buf);
    }

    // Convert a uint to string with a minimum number of digits.  If precision
    // is 0 and num is 0 then the result is the empty string. Could move this
    // to uint: but it doesn't seem all that useful.
589
    pub fn uint_to_str_prec(num: uint, radix: uint, prec: uint) -> ~str {
590 591 592
        return if prec == 0u && num == 0u {
                ~""
            } else {
593
                let s = num.to_str_radix(radix);
594
                let len = s.char_len();
595 596 597 598 599 600 601
                if len < prec {
                    let diff = prec - len;
                    let pad = str::from_chars(vec::from_elem(diff, '0'));
                    pad + s
                } else { s }
            };
    }
602
    pub fn get_int_precision(cv: Conv) -> uint {
603 604 605 606 607 608 609 610 611
        return match cv.precision {
              CountIs(c) => c as uint,
              CountImplied => 1u
            };
    }

    #[deriving(Eq)]
    pub enum PadMode { PadSigned, PadUnsigned, PadNozero, PadFloat }

612
    pub fn pad(cv: Conv, s: &str, head: Option<char>, mode: PadMode,
613 614 615 616
               buf: &mut ~str) {
        let headsize = match head { Some(_) => 1, _ => 0 };
        let uwidth : uint = match cv.width {
            CountImplied => {
D
Daniel Micay 已提交
617
                for &c in head.iter() {
618 619 620 621 622 623
                    buf.push_char(c);
                }
                return buf.push_str(s);
            }
            CountIs(width) => { width as uint }
        };
624
        let strlen = s.char_len() + headsize;
625
        if uwidth <= strlen {
D
Daniel Micay 已提交
626
            for &c in head.iter() {
627 628 629 630 631 632 633
                buf.push_char(c);
            }
            return buf.push_str(s);
        }
        let mut padchar = ' ';
        let diff = uwidth - strlen;
        if have_flag(cv.flags, flag_left_justify) {
D
Daniel Micay 已提交
634
            for &c in head.iter() {
635 636 637
                buf.push_char(c);
            }
            buf.push_str(s);
638
            do diff.times {
639 640 641 642 643 644 645 646 647 648
                buf.push_char(padchar);
            }
            return;
        }
        let (might_zero_pad, signed) = match mode {
          PadNozero   => (false, true),
          PadSigned   => (true, true),
          PadFloat    => (true, true),
          PadUnsigned => (true, false)
        };
649
        fn have_precision(cv: Conv) -> bool {
650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667
            return match cv.precision { CountImplied => false, _ => true };
        }
        let zero_padding = {
            if might_zero_pad && have_flag(cv.flags, flag_left_zero_pad) &&
                (!have_precision(cv) || mode == PadFloat) {
                padchar = '0';
                true
            } else {
                false
            }
        };
        let padstr = str::from_chars(vec::from_elem(diff, padchar));
        // This is completely heinous. If we have a signed value then
        // potentially rip apart the intermediate result and insert some
        // zeros. It may make sense to convert zero padding to a precision
        // instead.

        if signed && zero_padding {
D
Daniel Micay 已提交
668
            for &head in head.iter() {
669 670 671 672 673 674 675 676 677
                if head == '+' || head == '-' || head == ' ' {
                    buf.push_char(head);
                    buf.push_str(padstr);
                    buf.push_str(s);
                    return;
                }
            }
        }
        buf.push_str(padstr);
D
Daniel Micay 已提交
678
        for &c in head.iter() {
679 680 681 682
            buf.push_char(c);
        }
        buf.push_str(s);
    }
683
    #[inline]
684
    pub fn have_flag(flags: u32, f: u32) -> bool {
685 686 687 688
        flags & f != 0
    }
}

J
Jesse Jones 已提交
689
// Bulk of the tests are in src/test/run-pass/syntax-extension-fmt.rs
E
Eric Holk 已提交
690 691 692 693 694
#[cfg(test)]
mod test {
    #[test]
    fn fmt_slice() {
        let s = "abc";
P
Paul Stansifer 已提交
695
        let _s = fmt!("%s", s);
E
Eric Holk 已提交
696 697
    }
}