@@ -98,6 +98,7 @@ pub enum FormatType {
9898 GeneralFormatUpper ,
9999 FixedPointLower ,
100100 FixedPointUpper ,
101+ Percentage ,
101102}
102103
103104#[ derive( Debug , PartialEq ) ]
@@ -232,11 +233,12 @@ fn parse_format_type(text: &str) -> (Option<FormatType>, &str) {
232233 Some ( 'g' ) => ( Some ( FormatType :: GeneralFormatLower ) , chars. as_str ( ) ) ,
233234 Some ( 'G' ) => ( Some ( FormatType :: GeneralFormatUpper ) , chars. as_str ( ) ) ,
234235 Some ( 'n' ) => ( Some ( FormatType :: Number ) , chars. as_str ( ) ) ,
236+ Some ( '%' ) => ( Some ( FormatType :: Percentage ) , chars. as_str ( ) ) ,
235237 _ => ( None , text) ,
236238 }
237239}
238240
239- fn parse_format_spec ( text : & str ) -> FormatSpec {
241+ fn parse_format_spec ( text : & str ) -> Result < FormatSpec , & ' static str > {
240242 let ( preconversor, after_preconversor) = parse_preconversor ( text) ;
241243 let ( mut fill, mut align, after_align) = parse_fill_and_align ( after_preconversor) ;
242244 let ( sign, after_sign) = parse_sign ( after_align) ;
@@ -245,14 +247,17 @@ fn parse_format_spec(text: &str) -> FormatSpec {
245247 let ( width, after_width) = parse_number ( after_zero) ;
246248 let ( grouping_option, after_grouping_option) = parse_grouping_option ( after_width) ;
247249 let ( precision, after_precision) = parse_precision ( after_grouping_option) ;
248- let ( format_type, _) = parse_format_type ( after_precision) ;
250+ let ( format_type, after_format_type) = parse_format_type ( after_precision) ;
251+ if !after_format_type. is_empty ( ) {
252+ return Err ( "Invalid format specifier" ) ;
253+ }
249254
250255 if zero && fill. is_none ( ) {
251256 fill. replace ( '0' ) ;
252257 align = align. or ( Some ( FormatAlign :: AfterSign ) ) ;
253258 }
254259
255- FormatSpec {
260+ Ok ( FormatSpec {
256261 preconversor,
257262 fill,
258263 align,
@@ -262,11 +267,11 @@ fn parse_format_spec(text: &str) -> FormatSpec {
262267 grouping_option,
263268 precision,
264269 format_type,
265- }
270+ } )
266271}
267272
268273impl FormatSpec {
269- pub fn parse ( text : & str ) -> FormatSpec {
274+ pub fn parse ( text : & str ) -> Result < FormatSpec , & ' static str > {
270275 parse_format_spec ( text)
271276 }
272277
@@ -369,6 +374,11 @@ impl FormatSpec {
369374 Some ( FormatType :: ExponentLower ) => {
370375 Err ( "Format code 'e' for object of type 'float' not implemented yet" )
371376 }
377+ Some ( FormatType :: Percentage ) => match magnitude {
378+ magnitude if magnitude. is_nan ( ) => Ok ( "nan%" . to_string ( ) ) ,
379+ magnitude if magnitude. is_infinite ( ) => Ok ( "inf%" . to_string ( ) ) ,
380+ _ => Ok ( format ! ( "{:.*}%" , precision, magnitude * 100.0 ) ) ,
381+ } ,
372382 None => {
373383 match magnitude {
374384 magnitude if magnitude. is_nan ( ) => Ok ( "nan" . to_string ( ) ) ,
@@ -443,6 +453,9 @@ impl FormatSpec {
443453 _ => Err ( "Unable to convert int to float" ) ,
444454 }
445455 }
456+ Some ( FormatType :: Percentage ) => {
457+ Err ( "Format code '%' for object of type 'int' not implemented yet" )
458+ }
446459 None => Ok ( magnitude. to_str_radix ( 10 ) ) ,
447460 } ;
448461 if raw_magnitude_string_result. is_err ( ) {
@@ -525,7 +538,7 @@ pub enum FormatParseError {
525538impl FromStr for FormatSpec {
526539 type Err = & ' static str ;
527540 fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
528- Ok ( FormatSpec :: parse ( s) )
541+ FormatSpec :: parse ( s)
529542 }
530543}
531544
@@ -702,7 +715,7 @@ mod tests {
702715
703716 #[ test]
704717 fn test_width_only ( ) {
705- let expected = FormatSpec {
718+ let expected = Ok ( FormatSpec {
706719 preconversor : None ,
707720 fill : None ,
708721 align : None ,
@@ -712,13 +725,13 @@ mod tests {
712725 grouping_option : None ,
713726 precision : None ,
714727 format_type : None ,
715- } ;
728+ } ) ;
716729 assert_eq ! ( parse_format_spec( "33" ) , expected) ;
717730 }
718731
719732 #[ test]
720733 fn test_fill_and_width ( ) {
721- let expected = FormatSpec {
734+ let expected = Ok ( FormatSpec {
722735 preconversor : None ,
723736 fill : Some ( '<' ) ,
724737 align : Some ( FormatAlign :: Right ) ,
@@ -728,13 +741,13 @@ mod tests {
728741 grouping_option : None ,
729742 precision : None ,
730743 format_type : None ,
731- } ;
744+ } ) ;
732745 assert_eq ! ( parse_format_spec( "<>33" ) , expected) ;
733746 }
734747
735748 #[ test]
736749 fn test_all ( ) {
737- let expected = FormatSpec {
750+ let expected = Ok ( FormatSpec {
738751 preconversor : None ,
739752 fill : Some ( '<' ) ,
740753 align : Some ( FormatAlign :: Right ) ,
@@ -744,38 +757,52 @@ mod tests {
744757 grouping_option : Some ( FormatGrouping :: Comma ) ,
745758 precision : Some ( 11 ) ,
746759 format_type : Some ( FormatType :: Binary ) ,
747- } ;
760+ } ) ;
748761 assert_eq ! ( parse_format_spec( "<>-#23,.11b" ) , expected) ;
749762 }
750763
751764 #[ test]
752765 fn test_format_int ( ) {
753766 assert_eq ! (
754- parse_format_spec( "d" ) . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
767+ parse_format_spec( "d" )
768+ . unwrap( )
769+ . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
755770 Ok ( "16" . to_string( ) )
756771 ) ;
757772 assert_eq ! (
758- parse_format_spec( "x" ) . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
773+ parse_format_spec( "x" )
774+ . unwrap( )
775+ . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
759776 Ok ( "10" . to_string( ) )
760777 ) ;
761778 assert_eq ! (
762- parse_format_spec( "b" ) . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
779+ parse_format_spec( "b" )
780+ . unwrap( )
781+ . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
763782 Ok ( "10000" . to_string( ) )
764783 ) ;
765784 assert_eq ! (
766- parse_format_spec( "o" ) . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
785+ parse_format_spec( "o" )
786+ . unwrap( )
787+ . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
767788 Ok ( "20" . to_string( ) )
768789 ) ;
769790 assert_eq ! (
770- parse_format_spec( "+d" ) . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
791+ parse_format_spec( "+d" )
792+ . unwrap( )
793+ . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
771794 Ok ( "+16" . to_string( ) )
772795 ) ;
773796 assert_eq ! (
774- parse_format_spec( "^ 5d" ) . format_int( & BigInt :: from_bytes_be( Sign :: Minus , b"\x10 " ) ) ,
797+ parse_format_spec( "^ 5d" )
798+ . unwrap( )
799+ . format_int( & BigInt :: from_bytes_be( Sign :: Minus , b"\x10 " ) ) ,
775800 Ok ( " -16 " . to_string( ) )
776801 ) ;
777802 assert_eq ! (
778- parse_format_spec( "0>+#10x" ) . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
803+ parse_format_spec( "0>+#10x" )
804+ . unwrap( )
805+ . format_int( & BigInt :: from_bytes_be( Sign :: Plus , b"\x10 " ) ) ,
779806 Ok ( "00000+0x10" . to_string( ) )
780807 ) ;
781808 }
@@ -814,4 +841,15 @@ mod tests {
814841
815842 assert_eq ! ( FormatString :: from_str( "{{{key}}}ddfe" ) , expected) ;
816843 }
844+
845+ #[ test]
846+ fn test_format_invalid_specification ( ) {
847+ assert_eq ! ( parse_format_spec( "%3" ) , Err ( "Invalid format specifier" ) ) ;
848+ assert_eq ! ( parse_format_spec( ".2fa" ) , Err ( "Invalid format specifier" ) ) ;
849+ assert_eq ! ( parse_format_spec( "ds" ) , Err ( "Invalid format specifier" ) ) ;
850+ assert_eq ! ( parse_format_spec( "x+" ) , Err ( "Invalid format specifier" ) ) ;
851+ assert_eq ! ( parse_format_spec( "b4" ) , Err ( "Invalid format specifier" ) ) ;
852+ assert_eq ! ( parse_format_spec( "o!" ) , Err ( "Invalid format specifier" ) ) ;
853+ assert_eq ! ( parse_format_spec( "d " ) , Err ( "Invalid format specifier" ) ) ;
854+ }
817855}
0 commit comments