@@ -3,6 +3,49 @@ use num_traits::Signed;
33use std:: cmp;
44use std:: str:: FromStr ;
55
6+ #[ derive( Debug , Copy , Clone , PartialEq ) ]
7+ pub enum FormatPreconversor {
8+ Str ,
9+ Repr ,
10+ Ascii ,
11+ }
12+
13+ impl FormatPreconversor {
14+ pub fn from_char ( c : char ) -> Option < FormatPreconversor > {
15+ match c {
16+ 's' => Some ( FormatPreconversor :: Str ) ,
17+ 'r' => Some ( FormatPreconversor :: Repr ) ,
18+ 'a' => Some ( FormatPreconversor :: Ascii ) ,
19+ _ => None ,
20+ }
21+ }
22+
23+ pub fn from_str ( text : & str ) -> Option < FormatPreconversor > {
24+ let mut chars = text. chars ( ) ;
25+ if chars. next ( ) != Some ( '!' ) {
26+ return None ;
27+ }
28+
29+ match chars. next ( ) {
30+ None => None , // Should fail instead?
31+ Some ( c) => FormatPreconversor :: from_char ( c) ,
32+ }
33+ }
34+
35+ pub fn parse_and_consume ( text : & str ) -> ( Option < FormatPreconversor > , & str ) {
36+ let preconversor = FormatPreconversor :: from_str ( text) ;
37+ match preconversor {
38+ None => ( None , text) ,
39+ Some ( _) => {
40+ let mut chars = text. chars ( ) ;
41+ chars. next ( ) ; // Consume the bang
42+ chars. next ( ) ; // Consume one r,s,a char
43+ ( preconversor, chars. as_str ( ) )
44+ }
45+ }
46+ }
47+ }
48+
649#[ derive( Debug , Copy , Clone , PartialEq ) ]
750pub enum FormatAlign {
851 Left ,
@@ -56,6 +99,7 @@ pub enum FormatType {
5699
57100#[ derive( Debug , PartialEq ) ]
58101pub struct FormatSpec {
102+ preconversor : Option < FormatPreconversor > ,
59103 fill : Option < char > ,
60104 align : Option < FormatAlign > ,
61105 sign : Option < FormatSign > ,
@@ -75,6 +119,10 @@ fn get_num_digits(text: &str) -> usize {
75119 text. len ( )
76120}
77121
122+ fn parse_preconversor ( text : & str ) -> ( Option < FormatPreconversor > , & str ) {
123+ FormatPreconversor :: parse_and_consume ( text)
124+ }
125+
78126fn parse_align ( text : & str ) -> ( Option < FormatAlign > , & str ) {
79127 let mut chars = text. chars ( ) ;
80128 let maybe_align = chars. next ( ) . and_then ( FormatAlign :: from_char) ;
@@ -186,7 +234,8 @@ fn parse_format_type(text: &str) -> (Option<FormatType>, &str) {
186234}
187235
188236fn parse_format_spec ( text : & str ) -> FormatSpec {
189- let ( fill, align, after_align) = parse_fill_and_align ( text) ;
237+ let ( preconversor, after_preconversor) = parse_preconversor ( text) ;
238+ let ( fill, align, after_align) = parse_fill_and_align ( after_preconversor) ;
190239 let ( sign, after_sign) = parse_sign ( after_align) ;
191240 let ( alternate_form, after_alternate_form) = parse_alternate_form ( after_sign) ;
192241 let after_zero = parse_zero ( after_alternate_form) ;
@@ -196,6 +245,7 @@ fn parse_format_spec(text: &str) -> FormatSpec {
196245 let ( format_type, _) = parse_format_type ( after_precision) ;
197246
198247 FormatSpec {
248+ preconversor,
199249 fill,
200250 align,
201251 sign,
@@ -467,6 +517,18 @@ impl FormatString {
467517 String :: new ( )
468518 } ;
469519
520+ // On parts[0] can still be the preconversor (!r, !s, !a)
521+ let parts: Vec < & str > = arg_part. splitn ( 2 , '!' ) . collect ( ) ;
522+ // before the bang is a keyword or arg index, after the comma is maybe a conversor spec.
523+ let arg_part = parts[ 0 ] ;
524+
525+ let preconversor_spec = if parts. len ( ) > 1 {
526+ "!" . to_string ( ) + parts[ 1 ]
527+ } else {
528+ String :: new ( )
529+ } ;
530+ let format_spec = preconversor_spec + & format_spec;
531+
470532 if arg_part. is_empty ( ) {
471533 return Ok ( FormatPart :: AutoSpec ( format_spec) ) ;
472534 }
@@ -551,6 +613,7 @@ mod tests {
551613 #[ test]
552614 fn test_width_only ( ) {
553615 let expected = FormatSpec {
616+ preconversor : None ,
554617 fill : None ,
555618 align : None ,
556619 sign : None ,
@@ -566,6 +629,7 @@ mod tests {
566629 #[ test]
567630 fn test_fill_and_width ( ) {
568631 let expected = FormatSpec {
632+ preconversor : None ,
569633 fill : Some ( '<' ) ,
570634 align : Some ( FormatAlign :: Right ) ,
571635 sign : None ,
@@ -581,6 +645,7 @@ mod tests {
581645 #[ test]
582646 fn test_all ( ) {
583647 let expected = FormatSpec {
648+ preconversor : None ,
584649 fill : Some ( '<' ) ,
585650 align : Some ( FormatAlign :: Right ) ,
586651 sign : Some ( FormatSign :: Minus ) ,
0 commit comments