/// Enum describing where an argument for a format can be located.
#[derive(Copy, Clone, PartialEq)]
pub enum Position<'a> {
- /// The argument will be in the next position. This is the default.
- ArgumentNext,
/// The argument is located at a specific index.
ArgumentIs(usize),
/// The argument has a name.
CountIsName(&'a str),
/// The count is specified by the argument at the given index.
CountIsParam(usize),
- /// The count is specified by the next parameter.
- CountIsNextParam,
/// The count is implied and cannot be explicitly specified.
CountImplied,
}
cur: iter::Peekable<str::CharIndices<'a>>,
/// Error messages accumulated during parsing
pub errors: Vec<string::String>,
+ /// Current position of implicit positional argument pointer
+ curarg: usize,
}
impl<'a> Iterator for Parser<'a> {
input: s,
cur: s.char_indices().peekable(),
errors: vec![],
+ curarg: 0,
}
}
/// Parses an Argument structure, or what's contained within braces inside
/// the format string
fn argument(&mut self) -> Argument<'a> {
+ let pos = self.position();
+ let format = self.format();
+
+ // Resolve position after parsing format spec.
+ let pos = match pos {
+ Some(position) => position,
+ None => {
+ let i = self.curarg;
+ self.curarg += 1;
+ ArgumentIs(i)
+ }
+ };
+
Argument {
- position: self.position(),
- format: self.format(),
+ position: pos,
+ format: format,
}
}
/// Parses a positional argument for a format. This could either be an
/// integer index of an argument, a named argument, or a blank string.
- fn position(&mut self) -> Position<'a> {
+ /// Returns `Some(parsed_position)` if the position is not implicitly
+ /// consuming a macro argument, `None` if it's the case.
+ fn position(&mut self) -> Option<Position<'a>> {
if let Some(i) = self.integer() {
- ArgumentIs(i)
+ Some(ArgumentIs(i))
} else {
match self.cur.peek() {
- Some(&(_, c)) if c.is_alphabetic() => ArgumentNamed(self.word()),
- _ => ArgumentNext,
+ Some(&(_, c)) if c.is_alphabetic() => Some(ArgumentNamed(self.word())),
+
+ // This is an `ArgumentNext`.
+ // Record the fact and do the resolution after parsing the
+ // format spec, to make things like `{:.*}` work.
+ _ => None,
}
}
}
}
if self.consume('.') {
if self.consume('*') {
- spec.precision = CountIsNextParam;
+ // Resolve `CountIsNextParam`.
+ // We can do this immediately as `position` is resolved later.
+ let i = self.curarg;
+ self.curarg += 1;
+ spec.precision = CountIsParam(i);
} else {
spec.precision = self.count();
}
fn format_nothing() {
same("{}",
&[NextArgument(Argument {
- position: ArgumentNext,
+ position: ArgumentIs(0),
format: fmtdflt(),
})]);
}
fn format_counts() {
same("{:10s}",
&[NextArgument(Argument {
- position: ArgumentNext,
+ position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
})]);
same("{:10$.10s}",
&[NextArgument(Argument {
- position: ArgumentNext,
+ position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
})]);
same("{:.*s}",
&[NextArgument(Argument {
- position: ArgumentNext,
+ position: ArgumentIs(1),
format: FormatSpec {
fill: None,
align: AlignUnknown,
flags: 0,
- precision: CountIsNextParam,
+ precision: CountIsParam(0),
width: CountImplied,
ty: "s",
},
})]);
same("{:.10$s}",
&[NextArgument(Argument {
- position: ArgumentNext,
+ position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
})]);
same("{:a$.b$s}",
&[NextArgument(Argument {
- position: ArgumentNext,
+ position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
fn format_flags() {
same("{:-}",
&[NextArgument(Argument {
- position: ArgumentNext,
+ position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
})]);
same("{:+#}",
&[NextArgument(Argument {
- position: ArgumentNext,
+ position: ArgumentIs(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,