@@ -227,6 +227,7 @@ pub struct FormatSpec {
227227 conversion : Option < FormatConversion > ,
228228 fill : Option < CodePoint > ,
229229 align : Option < FormatAlign > ,
230+ align_specified : bool ,
230231 sign : Option < FormatSign > ,
231232 alternate_form : bool ,
232233 width : Option < usize > ,
@@ -346,6 +347,7 @@ impl FormatSpec {
346347 // get_integer in CPython
347348 let ( conversion, text) = FormatConversion :: parse ( text) ;
348349 let ( mut fill, mut align, text) = parse_fill_and_align ( text) ;
350+ let align_specified = align. is_some ( ) ;
349351 let ( sign, text) = FormatSign :: parse ( text) ;
350352 let ( alternate_form, text) = parse_alternate_form ( text) ;
351353 let ( zero, text) = parse_zero ( text) ;
@@ -374,6 +376,7 @@ impl FormatSpec {
374376 conversion,
375377 fill,
376378 align,
379+ align_specified,
377380 sign,
378381 alternate_form,
379382 width,
@@ -1020,13 +1023,24 @@ impl FormatSpec {
10201023 self . validate_format ( FormatType :: String ) ?;
10211024 match self . format_type {
10221025 Some ( FormatType :: String ) | None => {
1026+ if self . align == Some ( FormatAlign :: AfterSign ) && self . align_specified {
1027+ return Err ( FormatSpecError :: StringAlignmentFlag ) ;
1028+ }
10231029 // CPython parity: precision truncates BEFORE width pads.
10241030 // `'{:3.2s}'.format('abc')` -> 'ab ' (truncate to 'ab', pad to 3).
10251031 let truncated: String = match self . precision {
10261032 Some ( p) => s. deref ( ) . chars ( ) . take ( p) . collect ( ) ,
10271033 None => s. deref ( ) . to_owned ( ) ,
10281034 } ;
1029- Ok ( self . format_sign_and_align ( & truncated, "" , FormatAlign :: Left ) )
1035+ let spec = Self {
1036+ align : if self . align == Some ( FormatAlign :: AfterSign ) {
1037+ Some ( FormatAlign :: Left )
1038+ } else {
1039+ self . align
1040+ } ,
1041+ ..* self
1042+ } ;
1043+ Ok ( spec. format_sign_and_align ( & truncated, "" , FormatAlign :: Left ) )
10301044 }
10311045 _ => {
10321046 let ch = char:: from ( self . format_type . as_ref ( ) . unwrap ( ) ) ;
@@ -1260,6 +1274,7 @@ pub enum FormatSpecError {
12601274 CodeNotInRange ,
12611275 ZeroPadding ,
12621276 AlignmentFlag ,
1277+ StringAlignmentFlag ,
12631278 NotImplemented ( char , & ' static str ) ,
12641279}
12651280
@@ -1609,6 +1624,7 @@ mod tests {
16091624 conversion : None ,
16101625 fill : None ,
16111626 align : None ,
1627+ align_specified : false ,
16121628 sign : None ,
16131629 alternate_form : false ,
16141630 width : Some ( 33 ) ,
@@ -1626,6 +1642,7 @@ mod tests {
16261642 conversion : None ,
16271643 fill : Some ( '<' . into ( ) ) ,
16281644 align : Some ( FormatAlign :: Right ) ,
1645+ align_specified : true ,
16291646 sign : None ,
16301647 alternate_form : false ,
16311648 width : Some ( 33 ) ,
@@ -1643,6 +1660,7 @@ mod tests {
16431660 conversion : None ,
16441661 fill : Some ( '<' . into ( ) ) ,
16451662 align : Some ( FormatAlign :: Right ) ,
1663+ align_specified : true ,
16461664 sign : Some ( FormatSign :: Minus ) ,
16471665 alternate_form : true ,
16481666 width : Some ( 23 ) ,
@@ -1690,6 +1708,35 @@ mod tests {
16901708 assert_eq ! ( format_bool( "%" , false ) , Ok ( "0.000000%" . to_owned( ) ) ) ;
16911709 }
16921710
1711+ #[ test]
1712+ fn format_string_zero_padding_uses_left_alignment ( ) {
1713+ let spec = FormatSpec :: parse ( "08s" ) . unwrap ( ) ;
1714+ let value = "result" . to_owned ( ) ;
1715+
1716+ assert_eq ! ( spec. format_string( & value) , Ok ( "result00" . to_owned( ) ) ) ;
1717+ }
1718+
1719+ #[ test]
1720+ fn format_string_explicit_after_sign_alignment_is_invalid ( ) {
1721+ let spec = FormatSpec :: parse ( "=8s" ) . unwrap ( ) ;
1722+ let value = "result" . to_owned ( ) ;
1723+
1724+ assert_eq ! (
1725+ spec. format_string( & value) ,
1726+ Err ( FormatSpecError :: StringAlignmentFlag )
1727+ ) ;
1728+ }
1729+
1730+ #[ test]
1731+ fn format_int_zero_padding_stays_after_sign ( ) {
1732+ let spec = FormatSpec :: parse ( "08" ) . unwrap ( ) ;
1733+
1734+ assert_eq ! (
1735+ spec. format_int( & BigInt :: from( -42 ) ) ,
1736+ Ok ( "-0000042" . to_owned( ) )
1737+ ) ;
1738+ }
1739+
16931740 #[ test]
16941741 fn format_int ( ) {
16951742 assert_eq ! (
0 commit comments