@@ -223,7 +223,7 @@ fn parse_fill_and_align(text: &str) -> (Option<char>, Option<FormatAlign>, &str)
223223}
224224}
225225
226- fn parse_number ( text : & str ) ->Result < ( Option < usize > , & str ) , & ' static str > {
226+ fn parse_number ( text : & str ) ->Result < ( Option < usize > , & str ) , FormatSpecError > {
227227let num_digits: usize =get_num_digits ( text) ;
228228if num_digits ==0 {
229229return Ok ( ( None , text) ) ;
@@ -232,7 +232,7 @@ fn parse_number(text: &str) -> Result<(Option<usize>, &str), &'static str> {
232232Ok ( ( Some ( num) , & text[ num_digits..] ) )
233233} else {
234234// NOTE: this condition is different from CPython
235- Err ( "Too many decimal digits in format string" )
235+ Err ( FormatSpecError :: DecimalDigitsTooMany )
236236}
237237}
238238
@@ -252,14 +252,14 @@ fn parse_zero(text: &str) -> (bool, &str) {
252252}
253253}
254254
255- fn parse_precision ( text : & str ) ->Result < ( Option < usize > , & str ) , & ' static str > {
255+ fn parse_precision ( text : & str ) ->Result < ( Option < usize > , & str ) , FormatSpecError > {
256256let mut chars = text. chars ( ) ;
257257Ok ( match chars. next ( ) {
258258Some ( '.' ) =>{
259259let ( size, remaining) =parse_number ( chars. as_str ( ) ) ?;
260260if let Some ( size) = size{
261261if size > i32:: MAX as usize {
262- return Err ( "Precision too big" ) ;
262+ return Err ( FormatSpecError :: PrecisionTooBig ) ;
263263}
264264( Some ( size) , remaining)
265265} else {
@@ -271,7 +271,7 @@ fn parse_precision(text: &str) -> Result<(Option<usize>, &str), &'static str> {
271271}
272272
273273impl FormatSpec {
274- pub fn parse ( text : & str ) ->Result < Self , String > {
274+ pub fn parse ( text : & str ) ->Result < Self , FormatSpecError > {
275275// get_integer in CPython
276276let ( preconversor, text) =FormatPreconversor :: parse ( text) ;
277277let ( mut fill, mut align, text) =parse_fill_and_align ( text) ;
@@ -283,7 +283,7 @@ impl FormatSpec {
283283let ( precision, text) =parse_precision ( text) ?;
284284let ( format_type, text) =FormatType :: parse ( text) ;
285285if !text. is_empty ( ) {
286- return Err ( "Invalid format specifier" . to_owned ( ) ) ;
286+ return Err ( FormatSpecError :: InvalidFormatSpecifier ) ;
287287}
288288
289289if zero && fill. is_none ( ) {
@@ -359,7 +359,7 @@ impl FormatSpec {
359359 magnitude_str
360360}
361361
362- fn validate_format ( & self , default_format_type : FormatType ) ->Result < ( ) , String > {
362+ fn validate_format ( & self , default_format_type : FormatType ) ->Result < ( ) , FormatSpecError > {
363363let format_type =self . format_type . as_ref ( ) . unwrap_or ( & default_format_type) ;
364364match ( & self . grouping_option , format_type) {
365365(
@@ -373,14 +373,14 @@ impl FormatSpec {
373373 |FormatType :: Number ,
374374) =>{
375375let ch = char:: from ( format_type) ;
376- Err ( format ! ( "Cannot specify ',' with '{ch}'." ) )
376+ Err ( FormatSpecError :: UnspecifiedFormat ( ',' , ch ) )
377377}
378378(
379379Some ( FormatGrouping :: Underscore ) ,
380380FormatType :: String |FormatType :: Character |FormatType :: Number ,
381381) =>{
382382let ch = char:: from ( format_type) ;
383- Err ( format ! ( "Cannot specify '_' with '{ch}'." ) )
383+ Err ( FormatSpecError :: UnspecifiedFormat ( '_' , ch ) )
384384}
385385 _ =>Ok ( ( ) ) ,
386386}
@@ -422,11 +422,11 @@ impl FormatSpec {
422422}
423423}
424424
425- pub fn format_float ( & self , num : f64 ) ->Result < String , String > {
425+ pub fn format_float ( & self , num : f64 ) ->Result < String , FormatSpecError > {
426426self . validate_format ( FormatType :: FixedPointLower ) ?;
427427let precision =self . precision . unwrap_or ( 6 ) ;
428428let magnitude = num. abs ( ) ;
429- let raw_magnitude_str: Result < String , String > =match self . format_type {
429+ let raw_magnitude_str: Result < String , FormatSpecError > =match self . format_type {
430430Some ( FormatType :: FixedPointUpper ) =>Ok ( float_ops:: format_fixed (
431431 precision,
432432 magnitude,
@@ -445,13 +445,9 @@ impl FormatSpec {
445445 |Some ( FormatType :: String )
446446 |Some ( FormatType :: Character ) =>{
447447let ch = char:: from ( self . format_type . as_ref ( ) . unwrap ( ) ) ;
448- Err ( format ! (
449- "Unknown format code '{ch}' for object of type 'float'" ,
450- ) )
451- }
452- Some ( FormatType :: Number ) =>{
453- Err ( "Format code 'n' for object of type 'float' not implemented yet" . to_owned ( ) )
448+ Err ( FormatSpecError :: UnknownFormatCode ( ch, "float" ) )
454449}
450+ Some ( FormatType :: Number ) =>Err ( FormatSpecError :: NotImplemented ( 'n' , "float" ) ) ,
455451Some ( FormatType :: GeneralFormatUpper ) =>{
456452let precision =if precision ==0 { 1 } else { precision} ;
457453Ok ( float_ops:: format_general (
@@ -524,14 +520,14 @@ impl FormatSpec {
524520}
525521
526522#[ inline]
527- fn format_int_radix ( & self , magnitude : BigInt , radix : u32 ) ->Result < String , String > {
523+ fn format_int_radix ( & self , magnitude : BigInt , radix : u32 ) ->Result < String , FormatSpecError > {
528524match self . precision {
529- Some ( _) =>Err ( "Precision not allowed in integer format specifier" . to_owned ( ) ) ,
525+ Some ( _) =>Err ( FormatSpecError :: PrecisionNotAllowed ) ,
530526None =>Ok ( magnitude. to_str_radix ( radix) ) ,
531527}
532528}
533529
534- pub fn format_int ( & self , num : & BigInt ) ->Result < String , String > {
530+ pub fn format_int ( & self , num : & BigInt ) ->Result < String , FormatSpecError > {
535531self . validate_format ( FormatType :: Decimal ) ?;
536532let magnitude = num. abs ( ) ;
537533let prefix =if self . alternate_form {
@@ -545,34 +541,27 @@ impl FormatSpec {
545541} else {
546542""
547543} ;
548- let raw_magnitude_str: Result < String , String > =match self . format_type {
544+ let raw_magnitude_str: Result < String , FormatSpecError > =match self . format_type {
549545Some ( FormatType :: Binary ) =>self . format_int_radix ( magnitude, 2 ) ,
550546Some ( FormatType :: Decimal ) =>self . format_int_radix ( magnitude, 10 ) ,
551547Some ( FormatType :: Octal ) =>self . format_int_radix ( magnitude, 8 ) ,
552548Some ( FormatType :: HexLower ) =>self . format_int_radix ( magnitude, 16 ) ,
553549Some ( FormatType :: HexUpper ) =>match self . precision {
554- Some ( _) =>Err ( "Precision not allowed in integer format specifier" . to_owned ( ) ) ,
550+ Some ( _) =>Err ( FormatSpecError :: PrecisionNotAllowed ) ,
555551None =>{
556552let mut result = magnitude. to_str_radix ( 16 ) ;
557553 result. make_ascii_uppercase ( ) ;
558554Ok ( result)
559555}
560556} ,
561557Some ( FormatType :: Number ) =>self . format_int_radix ( magnitude, 10 ) ,
562- Some ( FormatType :: String ) =>{
563- Err ( "Unknown format code 's' for object of type 'int'" . to_owned ( ) )
564- }
558+ Some ( FormatType :: String ) =>Err ( FormatSpecError :: UnknownFormatCode ( 's' , "int" ) ) ,
565559Some ( FormatType :: Character ) =>match ( self . sign , self . alternate_form ) {
566- ( Some ( _) , _) =>{
567- Err ( "Sign not allowed with integer format specifier 'c'" . to_owned ( ) )
568- }
569- ( _, true ) =>Err (
570- "Alternate form (#) not allowed with integer format specifier 'c'" . to_owned ( ) ,
571- ) ,
560+ ( Some ( _) , _) =>Err ( FormatSpecError :: NotAllowed ( "Sign" ) ) ,
561+ ( _, true ) =>Err ( FormatSpecError :: NotAllowed ( "Alternate form (#)" ) ) ,
572562( _, _) =>match num. to_u32 ( ) {
573563Some ( n) if n <=0x10ffff =>Ok ( std:: char:: from_u32 ( n) . unwrap ( ) . to_string ( ) ) ,
574- // TODO: raise OverflowError
575- Some ( _) |None =>Err ( "%c arg not in range(0x110000)" . to_owned ( ) ) ,
564+ Some ( _) |None =>Err ( FormatSpecError :: CodeNotInRange ) ,
576565} ,
577566} ,
578567Some ( FormatType :: GeneralFormatUpper )
@@ -583,7 +572,7 @@ impl FormatSpec {
583572 |Some ( FormatType :: ExponentLower )
584573 |Some ( FormatType :: Percentage ) =>match num. to_f64 ( ) {
585574Some ( float) =>return self . format_float ( float) ,
586- _ =>Err ( "Unable to convert int to float" . to_owned ( ) ) ,
575+ _ =>Err ( FormatSpecError :: UnableToConvert ) ,
587576} ,
588577None =>self . format_int_radix ( magnitude, 10 ) ,
589578} ;
@@ -605,7 +594,7 @@ impl FormatSpec {
605594)
606595}
607596
608- pub fn format_string ( & self , s : & BorrowedStr ) ->Result < String , String > {
597+ pub fn format_string ( & self , s : & BorrowedStr ) ->Result < String , FormatSpecError > {
609598self . validate_format ( FormatType :: String ) ?;
610599match self . format_type {
611600Some ( FormatType :: String ) |None =>self
@@ -618,9 +607,7 @@ impl FormatSpec {
618607} ) ,
619608 _ =>{
620609let ch = char:: from ( self . format_type . as_ref ( ) . unwrap ( ) ) ;
621- Err ( format ! (
622- "Unknown format code '{ch}' for object of type 'str'" ,
623- ) )
610+ Err ( FormatSpecError :: UnknownFormatCode ( ch, "str" ) )
624611}
625612}
626613}
@@ -630,7 +617,7 @@ impl FormatSpec {
630617magnitude_str : & BorrowedStr ,
631618sign_str : & str ,
632619default_align : FormatAlign ,
633- ) ->Result < String , String > {
620+ ) ->Result < String , FormatSpecError > {
634621let align =self . align . unwrap_or ( default_align) ;
635622
636623let num_chars = magnitude_str. char_len ( ) ;
@@ -670,6 +657,20 @@ impl FormatSpec {
670657}
671658}
672659
660+ #[ derive( Debug , PartialEq ) ]
661+ pub enum FormatSpecError {
662+ DecimalDigitsTooMany ,
663+ PrecisionTooBig ,
664+ InvalidFormatSpecifier ,
665+ UnspecifiedFormat ( char , char ) ,
666+ UnknownFormatCode ( char , & ' static str ) ,
667+ PrecisionNotAllowed ,
668+ NotAllowed ( & ' static str ) ,
669+ UnableToConvert ,
670+ CodeNotInRange ,
671+ NotImplemented ( char , & ' static str ) ,
672+ }
673+
673674#[ derive( Debug , PartialEq ) ]
674675pub enum FormatParseError {
675676UnmatchedBracket ,
@@ -683,7 +684,7 @@ pub enum FormatParseError {
683684}
684685
685686impl FromStr for FormatSpec {
686- type Err =String ;
687+ type Err =FormatSpecError ;
687688fn from_str ( s : & str ) ->Result < Self , Self :: Err > {
688689FormatSpec :: parse ( s)
689690}
@@ -1104,31 +1105,31 @@ mod tests {
11041105fn test_format_invalid_specification ( ) {
11051106assert_eq ! (
11061107FormatSpec :: parse( "%3" ) ,
1107- Err ( "Invalid format specifier" . to_owned ( ) )
1108+ Err ( FormatSpecError :: InvalidFormatSpecifier )
11081109) ;
11091110assert_eq ! (
11101111FormatSpec :: parse( ".2fa" ) ,
1111- Err ( "Invalid format specifier" . to_owned ( ) )
1112+ Err ( FormatSpecError :: InvalidFormatSpecifier )
11121113) ;
11131114assert_eq ! (
11141115FormatSpec :: parse( "ds" ) ,
1115- Err ( "Invalid format specifier" . to_owned ( ) )
1116+ Err ( FormatSpecError :: InvalidFormatSpecifier )
11161117) ;
11171118assert_eq ! (
11181119FormatSpec :: parse( "x+" ) ,
1119- Err ( "Invalid format specifier" . to_owned ( ) )
1120+ Err ( FormatSpecError :: InvalidFormatSpecifier )
11201121) ;
11211122assert_eq ! (
11221123FormatSpec :: parse( "b4" ) ,
1123- Err ( "Invalid format specifier" . to_owned ( ) )
1124+ Err ( FormatSpecError :: InvalidFormatSpecifier )
11241125) ;
11251126assert_eq ! (
11261127FormatSpec :: parse( "o!" ) ,
1127- Err ( "Invalid format specifier" . to_owned ( ) )
1128+ Err ( FormatSpecError :: InvalidFormatSpecifier )
11281129) ;
11291130assert_eq ! (
11301131FormatSpec :: parse( "d " ) ,
1131- Err ( "Invalid format specifier" . to_owned ( ) )
1132+ Err ( FormatSpecError :: InvalidFormatSpecifier )
11321133) ;
11331134}
11341135