@@ -316,7 +316,7 @@ impl Spec {
316316 match self {
317317 Self :: Char { width, align_left } => {
318318 let ( width, neg_width) =
319- resolve_asterisk_maybe_negative ( * width, & mut args) . unwrap_or_default ( ) ;
319+ resolve_asterisk_width ( * width, & mut args) . unwrap_or_default ( ) ;
320320 write_padded ( writer, & [ args. get_char ( ) ] , width, * align_left || neg_width)
321321 }
322322 Self :: String {
@@ -325,15 +325,15 @@ impl Spec {
325325 precision,
326326 } => {
327327 let ( width, neg_width) =
328- resolve_asterisk_maybe_negative ( * width, & mut args) . unwrap_or_default ( ) ;
328+ resolve_asterisk_width ( * width, & mut args) . unwrap_or_default ( ) ;
329329
330330 // GNU does do this truncation on a byte level, see for instance:
331331 // printf "%.1s" 🙃
332332 // > �
333333 // For now, we let printf panic when we truncate within a code point.
334334 // TODO: We need to not use Rust's formatting for aligning the output,
335335 // so that we can just write bytes to stdout without panicking.
336- let precision = resolve_asterisk ( * precision, & mut args) ;
336+ let precision = resolve_asterisk_precision ( * precision, & mut args) ;
337337 let s = args. get_str ( ) ;
338338 let truncated = match precision {
339339 Some ( p) if p < s. len ( ) => & s[ ..p] ,
@@ -349,7 +349,7 @@ impl Spec {
349349 Self :: EscapedString => {
350350 let s = args. get_str ( ) ;
351351 let mut parsed = Vec :: new ( ) ;
352- for c in parse_escape_only ( s. as_bytes ( ) , OctalParsing :: default ( ) ) {
352+ for c in parse_escape_only ( s. as_bytes ( ) , OctalParsing :: ThreeDigits ) {
353353 match c. write ( & mut parsed) ? {
354354 ControlFlow :: Continue ( ( ) ) => { }
355355 ControlFlow :: Break ( ( ) ) => {
@@ -382,8 +382,10 @@ impl Spec {
382382 positive_sign,
383383 alignment,
384384 } => {
385- let width = resolve_asterisk ( * width, & mut args) . unwrap_or ( 0 ) ;
386- let precision = resolve_asterisk ( * precision, & mut args) . unwrap_or ( 0 ) ;
385+ let ( width, neg_width) =
386+ resolve_asterisk_width ( * width, & mut args) . unwrap_or ( ( 0 , false ) ) ;
387+ let precision =
388+ resolve_asterisk_precision ( * precision, & mut args) . unwrap_or_default ( ) ;
387389 let i = args. get_i64 ( ) ;
388390
389391 if precision as u64 > i32:: MAX as u64 {
@@ -394,7 +396,11 @@ impl Spec {
394396 width,
395397 precision,
396398 positive_sign : * positive_sign,
397- alignment : * alignment,
399+ alignment : if neg_width {
400+ NumberAlignment :: Left
401+ } else {
402+ * alignment
403+ } ,
398404 }
399405 . fmt ( writer, i)
400406 . map_err ( FormatError :: IoError )
@@ -405,8 +411,10 @@ impl Spec {
405411 precision,
406412 alignment,
407413 } => {
408- let width = resolve_asterisk ( * width, & mut args) . unwrap_or ( 0 ) ;
409- let precision = resolve_asterisk ( * precision, & mut args) . unwrap_or ( 0 ) ;
414+ let ( width, neg_width) =
415+ resolve_asterisk_width ( * width, & mut args) . unwrap_or ( ( 0 , false ) ) ;
416+ let precision =
417+ resolve_asterisk_precision ( * precision, & mut args) . unwrap_or_default ( ) ;
410418 let i = args. get_u64 ( ) ;
411419
412420 if precision as u64 > i32:: MAX as u64 {
@@ -417,7 +425,11 @@ impl Spec {
417425 variant : * variant,
418426 precision,
419427 width,
420- alignment : * alignment,
428+ alignment : if neg_width {
429+ NumberAlignment :: Left
430+ } else {
431+ * alignment
432+ } ,
421433 }
422434 . fmt ( writer, i)
423435 . map_err ( FormatError :: IoError )
@@ -431,8 +443,9 @@ impl Spec {
431443 alignment,
432444 precision,
433445 } => {
434- let width = resolve_asterisk ( * width, & mut args) . unwrap_or ( 0 ) ;
435- let precision = resolve_asterisk ( * precision, & mut args) ;
446+ let ( width, neg_width) =
447+ resolve_asterisk_width ( * width, & mut args) . unwrap_or ( ( 0 , false ) ) ;
448+ let precision = resolve_asterisk_precision ( * precision, & mut args) ;
436449 let f: ExtendedBigDecimal = args. get_extended_big_decimal ( ) ;
437450
438451 if precision. is_some_and ( |p| p as u64 > i32:: MAX as u64 ) {
@@ -448,7 +461,11 @@ impl Spec {
448461 case : * case,
449462 force_decimal : * force_decimal,
450463 positive_sign : * positive_sign,
451- alignment : * alignment,
464+ alignment : if neg_width {
465+ NumberAlignment :: Left
466+ } else {
467+ * alignment
468+ } ,
452469 }
453470 . fmt ( writer, & f)
454471 . map_err ( FormatError :: IoError )
@@ -457,18 +474,9 @@ impl Spec {
457474 }
458475}
459476
460- fn resolve_asterisk < ' a > (
461- option : Option < CanAsterisk < usize > > ,
462- mut args : impl ArgumentIter < ' a > ,
463- ) -> Option < usize > {
464- match option {
465- None => None ,
466- Some ( CanAsterisk :: Asterisk ) => Some ( usize:: try_from ( args. get_u64 ( ) ) . ok ( ) . unwrap_or ( 0 ) ) ,
467- Some ( CanAsterisk :: Fixed ( w) ) => Some ( w) ,
468- }
469- }
470-
471- fn resolve_asterisk_maybe_negative < ' a > (
477+ /// Determine the width, potentially getting a value from args
478+ /// Returns the non-negative width and whether the value should be left-aligned.
479+ fn resolve_asterisk_width < ' a > (
472480 option : Option < CanAsterisk < usize > > ,
473481 mut args : impl ArgumentIter < ' a > ,
474482) -> Option < ( usize , bool ) > {
@@ -486,6 +494,23 @@ fn resolve_asterisk_maybe_negative<'a>(
486494 }
487495}
488496
497+ /// Determines the precision, which should (if defined)
498+ /// be a non-negative number.
499+ fn resolve_asterisk_precision < ' a > (
500+ option : Option < CanAsterisk < usize > > ,
501+ mut args : impl ArgumentIter < ' a > ,
502+ ) -> Option < usize > {
503+ match option {
504+ None => None ,
505+ Some ( CanAsterisk :: Asterisk ) => match args. get_i64 ( ) {
506+ v if v >= 0 => usize:: try_from ( v) . ok ( ) ,
507+ v if v < 0 => Some ( 0usize ) ,
508+ _ => None ,
509+ } ,
510+ Some ( CanAsterisk :: Fixed ( w) ) => Some ( w) ,
511+ }
512+ }
513+
489514fn write_padded (
490515 mut writer : impl Write ,
491516 text : & [ u8 ] ,
@@ -527,3 +552,110 @@ fn eat_number(rest: &mut &[u8], index: &mut usize) -> Option<usize> {
527552 }
528553 }
529554}
555+
556+ #[ cfg( test) ]
557+ mod tests {
558+ use super :: * ;
559+
560+ mod resolve_asterisk_width {
561+ use super :: * ;
562+ use crate :: format:: FormatArgument ;
563+
564+ #[ test]
565+ fn no_width ( ) {
566+ assert_eq ! ( None , resolve_asterisk_width( None , Vec :: new( ) . iter( ) ) ) ;
567+ }
568+
569+ #[ test]
570+ fn fixed_width ( ) {
571+ assert_eq ! (
572+ Some ( ( 42 , false ) ) ,
573+ resolve_asterisk_width( Some ( CanAsterisk :: Fixed ( 42 ) ) , Vec :: new( ) . iter( ) )
574+ ) ;
575+ }
576+
577+ #[ test]
578+ fn asterisks_with_numbers ( ) {
579+ assert_eq ! (
580+ Some ( ( 42 , false ) ) ,
581+ resolve_asterisk_width(
582+ Some ( CanAsterisk :: Asterisk ) ,
583+ vec![ FormatArgument :: SignedInt ( 42 ) ] . iter( )
584+ )
585+ ) ;
586+ assert_eq ! (
587+ Some ( ( 42 , false ) ) ,
588+ resolve_asterisk_width(
589+ Some ( CanAsterisk :: Asterisk ) ,
590+ vec![ FormatArgument :: Unparsed ( "42" . to_string( ) ) ] . iter( )
591+ )
592+ ) ;
593+
594+ assert_eq ! (
595+ Some ( ( 42 , true ) ) ,
596+ resolve_asterisk_width(
597+ Some ( CanAsterisk :: Asterisk ) ,
598+ vec![ FormatArgument :: SignedInt ( -42 ) ] . iter( )
599+ )
600+ ) ;
601+ assert_eq ! (
602+ Some ( ( 42 , true ) ) ,
603+ resolve_asterisk_width(
604+ Some ( CanAsterisk :: Asterisk ) ,
605+ vec![ FormatArgument :: Unparsed ( "-42" . to_string( ) ) ] . iter( )
606+ )
607+ ) ;
608+ }
609+ }
610+
611+ mod resolve_asterisk_precision {
612+ use super :: * ;
613+ use crate :: format:: FormatArgument ;
614+
615+ #[ test]
616+ fn no_width ( ) {
617+ assert_eq ! ( None , resolve_asterisk_precision( None , Vec :: new( ) . iter( ) ) ) ;
618+ }
619+
620+ #[ test]
621+ fn fixed_width ( ) {
622+ assert_eq ! (
623+ Some ( 42 ) ,
624+ resolve_asterisk_precision( Some ( CanAsterisk :: Fixed ( 42 ) ) , Vec :: new( ) . iter( ) )
625+ ) ;
626+ }
627+
628+ #[ test]
629+ fn asterisks_with_numbers ( ) {
630+ assert_eq ! (
631+ Some ( 42 ) ,
632+ resolve_asterisk_precision(
633+ Some ( CanAsterisk :: Asterisk ) ,
634+ vec![ FormatArgument :: SignedInt ( 42 ) ] . iter( )
635+ )
636+ ) ;
637+ assert_eq ! (
638+ Some ( 42 ) ,
639+ resolve_asterisk_precision(
640+ Some ( CanAsterisk :: Asterisk ) ,
641+ vec![ FormatArgument :: Unparsed ( "42" . to_string( ) ) ] . iter( )
642+ )
643+ ) ;
644+
645+ assert_eq ! (
646+ Some ( 0 ) ,
647+ resolve_asterisk_precision(
648+ Some ( CanAsterisk :: Asterisk ) ,
649+ vec![ FormatArgument :: SignedInt ( -42 ) ] . iter( )
650+ )
651+ ) ;
652+ assert_eq ! (
653+ Some ( 0 ) ,
654+ resolve_asterisk_precision(
655+ Some ( CanAsterisk :: Asterisk ) ,
656+ vec![ FormatArgument :: Unparsed ( "-42" . to_string( ) ) ] . iter( )
657+ )
658+ ) ;
659+ }
660+ }
661+ }
0 commit comments