@@ -103,7 +103,12 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
103
103
output. push ( chr) ;
104
104
extracted_expressions. push ( Arg :: Placeholder ) ;
105
105
state = State :: NotArg ;
106
- }
106
+ } ,
107
+ ( State :: MaybeArg , ':' ) => {
108
+ output. push ( chr) ;
109
+ extracted_expressions. push ( Arg :: Placeholder ) ;
110
+ state = State :: FormatOpts ;
111
+ } ,
107
112
( State :: MaybeArg , _) => {
108
113
if matches ! ( chr, '\\' | '$' ) {
109
114
current_expr. push ( '\\' ) ;
@@ -117,49 +122,40 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec<Arg>), ()> {
117
122
} else {
118
123
state = State :: Expr ;
119
124
}
120
- }
121
- ( State :: Ident | State :: Expr , '}' ) => {
122
- if inexpr_open_count == 0 {
123
- output. push ( chr) ;
124
-
125
- if matches ! ( state, State :: Expr ) {
126
- extracted_expressions. push ( Arg :: Expr ( current_expr. trim ( ) . into ( ) ) ) ;
127
- } else {
128
- extracted_expressions. push ( Arg :: Ident ( current_expr. trim ( ) . into ( ) ) ) ;
129
- }
130
-
131
- current_expr = String :: new ( ) ;
132
- state = State :: NotArg ;
133
- } else {
134
- // We're closing one brace met before inside of the expression.
135
- current_expr. push ( chr) ;
136
- inexpr_open_count -= 1 ;
137
- }
138
- }
125
+ } ,
139
126
( State :: Ident | State :: Expr , ':' ) if matches ! ( chars. peek( ) , Some ( ':' ) ) => {
140
127
// path separator
141
128
state = State :: Expr ;
142
129
current_expr. push_str ( "::" ) ;
143
130
chars. next ( ) ;
144
- }
145
- ( State :: Ident | State :: Expr , ':' ) => {
131
+ } ,
132
+ ( State :: Ident | State :: Expr , ':' | '}' ) => {
146
133
if inexpr_open_count == 0 {
147
- // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
148
- output. push ( chr) ;
134
+ let trimmed = current_expr. trim ( ) ;
149
135
150
- if matches ! ( state, State :: Expr ) {
151
- extracted_expressions. push ( Arg :: Expr ( current_expr. trim ( ) . into ( ) ) ) ;
136
+ // if the expression consists of a single number, like "0" or "12", it can refer to
137
+ // format args in the order they are specified.
138
+ // see: https://doc.rust-lang.org/std/fmt/#positional-parameters
139
+ if trimmed. chars ( ) . fold ( true , |only_num, c| c. is_ascii_digit ( ) && only_num) {
140
+ output. push_str ( trimmed) ;
141
+ } else if matches ! ( state, State :: Expr ) {
142
+ extracted_expressions. push ( Arg :: Expr ( trimmed. into ( ) ) ) ;
152
143
} else {
153
- extracted_expressions. push ( Arg :: Ident ( current_expr . trim ( ) . into ( ) ) ) ;
144
+ extracted_expressions. push ( Arg :: Ident ( trimmed . into ( ) ) ) ;
154
145
}
155
146
156
- current_expr = String :: new ( ) ;
157
- state = State :: FormatOpts ;
158
- } else {
147
+ output. push ( chr) ;
148
+ current_expr. clear ( ) ;
149
+ state = if chr == ':' { State :: FormatOpts } else if chr == '}' { State :: NotArg } else { unreachable ! ( ) } ;
150
+ } else if chr == '}' {
151
+ // We're closing one brace met before inside of the expression.
152
+ current_expr. push ( chr) ;
153
+ inexpr_open_count -= 1 ;
154
+ } else if chr == ':' {
159
155
// We're inside of braced expression, assume that it's a struct field name/value delimiter.
160
156
current_expr. push ( chr) ;
161
157
}
162
- }
158
+ } ,
163
159
( State :: Ident | State :: Expr , '{' ) => {
164
160
state = State :: Expr ;
165
161
current_expr. push ( chr) ;
@@ -219,6 +215,10 @@ mod tests {
219
215
( "{expr} is {2 + 2}" , expect ! [ [ "{} is {}; expr, 2 + 2" ] ] ) ,
220
216
( "{expr:?}" , expect ! [ [ "{:?}; expr" ] ] ) ,
221
217
( "{expr:1$}" , expect ! [ [ r"{:1\$}; expr" ] ] ) ,
218
+ ( "{:1$}" , expect ! [ [ r"{:1\$}; $1" ] ] ) ,
219
+ ( "{:>padding$}" , expect ! [ [ r"{:>padding\$}; $1" ] ] ) ,
220
+ ( "{}, {}, {0}" , expect ! [ [ r"{}, {}, {0}; $1, $2" ] ] ) ,
221
+ ( "{}, {}, {0:b}" , expect ! [ [ r"{}, {}, {0:b}; $1, $2" ] ] ) ,
222
222
( "{$0}" , expect ! [ [ r"{}; \$0" ] ] ) ,
223
223
( "{malformed" , expect ! [ [ "-" ] ] ) ,
224
224
( "malformed}" , expect ! [ [ "-" ] ] ) ,
0 commit comments