@@ -127,6 +127,15 @@ impl FormatParse for FormatGrouping {
127127 }
128128}
129129
130+ impl From < & FormatGrouping > for char {
131+ fn from ( fg : & FormatGrouping ) -> Self {
132+ match fg {
133+ FormatGrouping :: Comma => ',' ,
134+ FormatGrouping :: Underscore => '_' ,
135+ }
136+ }
137+ }
138+
130139#[ derive( Debug , PartialEq ) ]
131140pub enum FormatType {
132141 String ,
@@ -281,6 +290,7 @@ impl FormatSpec {
281290 pub fn parse ( text : impl AsRef < Wtf8 > ) -> Result < Self , FormatSpecError > {
282291 Self :: _parse ( text. as_ref ( ) )
283292 }
293+
284294 fn _parse ( text : & Wtf8 ) -> Result < Self , FormatSpecError > {
285295 // get_integer in CPython
286296 let ( conversion, text) = FormatConversion :: parse ( text) ;
@@ -290,6 +300,9 @@ impl FormatSpec {
290300 let ( zero, text) = parse_zero ( text) ;
291301 let ( width, text) = parse_number ( text) ?;
292302 let ( grouping_option, text) = FormatGrouping :: parse ( text) ;
303+ if let Some ( grouping) = & grouping_option {
304+ Self :: validate_separator ( grouping, text) ?;
305+ }
293306 let ( precision, text) = parse_precision ( text) ?;
294307 let ( format_type, text) = FormatType :: parse ( text) ;
295308 if !text. is_empty ( ) {
@@ -314,6 +327,20 @@ impl FormatSpec {
314327 } )
315328 }
316329
330+ fn validate_separator ( grouping : & FormatGrouping , text : & Wtf8 ) -> Result < ( ) , FormatSpecError > {
331+ let mut chars = text. code_points ( ) . peekable ( ) ;
332+ match chars. peek ( ) . and_then ( |cp| CodePoint :: to_char ( * cp) ) {
333+ Some ( c) if c == ',' || c == '_' => {
334+ if c == char:: from ( grouping) {
335+ Err ( FormatSpecError :: UnspecifiedFormat ( c, c) )
336+ } else {
337+ Err ( FormatSpecError :: ExclusiveFormat ( ',' , '_' ) )
338+ }
339+ }
340+ _ => Ok ( ( ) ) ,
341+ }
342+ }
343+
317344 fn compute_fill_string ( fill_char : CodePoint , fill_chars_needed : i32 ) -> Wtf8Buf {
318345 ( 0 ..fill_chars_needed) . map ( |_| fill_char) . collect ( )
319346 }
@@ -406,10 +433,7 @@ impl FormatSpec {
406433 fn add_magnitude_separators ( & self , magnitude_str : String , prefix : & str ) -> String {
407434 match & self . grouping_option {
408435 Some ( fg) => {
409- let sep = match fg {
410- FormatGrouping :: Comma => ',' ,
411- FormatGrouping :: Underscore => '_' ,
412- } ;
436+ let sep = char:: from ( fg) ;
413437 let inter = self . get_separator_interval ( ) . try_into ( ) . unwrap ( ) ;
414438 let magnitude_len = magnitude_str. len ( ) ;
415439 let width = self . width . unwrap_or ( magnitude_len) as i32 - prefix. len ( ) as i32 ;
@@ -720,10 +744,7 @@ impl FormatSpec {
720744 } ?;
721745 match & self . grouping_option {
722746 Some ( fg) => {
723- let sep = match fg {
724- FormatGrouping :: Comma => ',' ,
725- FormatGrouping :: Underscore => '_' ,
726- } ;
747+ let sep = char:: from ( fg) ;
727748 let inter = self . get_separator_interval ( ) . try_into ( ) . unwrap ( ) ;
728749 let len = magnitude_str. len ( ) as i32 ;
729750 let separated_magnitude =
@@ -818,6 +839,7 @@ pub enum FormatSpecError {
818839 PrecisionTooBig ,
819840 InvalidFormatSpecifier ,
820841 UnspecifiedFormat ( char , char ) ,
842+ ExclusiveFormat ( char , char ) ,
821843 UnknownFormatCode ( char , & ' static str ) ,
822844 PrecisionNotAllowed ,
823845 NotAllowed ( & ' static str ) ,
0 commit comments