| ... | ... | @@ -20,11 +20,14 @@ pub const Alignment = enum { |
| 20 | 20 | right, |
| 21 | 21 | }; |
| 22 | 22 | |
| 23 | const default_alignment = .right; |
| 24 | const default_fill_char = ' '; |
| 25 | |
| 23 | 26 | pub const FormatOptions = struct { |
| 24 | 27 | precision: ?usize = null, |
| 25 | 28 | width: ?usize = null, |
| 26 | | alignment: Alignment = .right, |
| 27 | | fill: u21 = ' ', |
| 29 | alignment: Alignment = default_alignment, |
| 30 | fill: u21 = default_fill_char, |
| 28 | 31 | }; |
| 29 | 32 | |
| 30 | 33 | /// Renders fmt string with args, calling `writer` with slices of bytes. |
| ... | ... | @@ -48,8 +51,8 @@ pub const FormatOptions = struct { |
| 48 | 51 | /// |
| 49 | 52 | /// Note that most of the parameters are optional and may be omitted. Also you can leave out separators like `:` and `.` when |
| 50 | 53 | /// all parameters after the separator are omitted. |
| 51 | | /// Only exception is the *fill* parameter. If *fill* is required, one has to specify *alignment* as well, as otherwise |
| 52 | | /// the digits after `:` is interpreted as *width*, not *fill*. |
| 54 | /// Only exception is the *fill* parameter. If a non-zero *fill* character is required at the same time as *width* is specified, |
| 55 | /// one has to specify *alignment* as well, as otherwise the digit following `:` is interpreted as *width*, not *fill*. |
| 53 | 56 | /// |
| 54 | 57 | /// The *specifier* has several options for types: |
| 55 | 58 | /// - `x` and `X`: output numeric value in hexadecimal notation |
| ... | ... | @@ -239,29 +242,38 @@ pub const Placeholder = struct { |
| 239 | 242 | } |
| 240 | 243 | } |
| 241 | 244 | |
| 242 | | // Parse the fill character |
| 243 | | // The fill parameter requires the alignment parameter to be specified |
| 244 | | // too |
| 245 | | const fill = comptime if (parser.peek(1)) |ch| |
| 245 | // Parse the fill character, if present. |
| 246 | // When the width field is also specified, the fill character must |
| 247 | // be followed by an alignment specifier, unless it's '0' (zero) |
| 248 | // (in which case it's handled as part of the width specifier) |
| 249 | var fill: ?u21 = comptime if (parser.peek(1)) |ch| |
| 246 | 250 | switch (ch) { |
| 247 | | '<', '^', '>' => parser.char().?, |
| 248 | | else => ' ', |
| 251 | '<', '^', '>' => parser.char(), |
| 252 | else => null, |
| 249 | 253 | } |
| 250 | 254 | else |
| 251 | | ' '; |
| 255 | null; |
| 252 | 256 | |
| 253 | 257 | // Parse the alignment parameter |
| 254 | | const alignment: Alignment = comptime if (parser.peek(0)) |ch| init: { |
| 258 | const alignment: ?Alignment = comptime if (parser.peek(0)) |ch| init: { |
| 255 | 259 | switch (ch) { |
| 256 | | '<', '^', '>' => _ = parser.char(), |
| 257 | | else => {}, |
| 260 | '<', '^', '>' => { |
| 261 | // consume the character |
| 262 | break :init switch (parser.char().?) { |
| 263 | '<' => .left, |
| 264 | '^' => .center, |
| 265 | else => .right, |
| 266 | }; |
| 267 | }, |
| 268 | else => break :init null, |
| 258 | 269 | } |
| 259 | | break :init switch (ch) { |
| 260 | | '<' => .left, |
| 261 | | '^' => .center, |
| 262 | | else => .right, |
| 263 | | }; |
| 264 | | } else .right; |
| 270 | } else null; |
| 271 | |
| 272 | // When none of the fill character and the alignment specifier have |
| 273 | // been provided, check whether the width starts with a zero. |
| 274 | if (fill == null and alignment == null) { |
| 275 | fill = comptime if (parser.peek(0) == '0') '0' else null; |
| 276 | } |
| 265 | 277 | |
| 266 | 278 | // Parse the width parameter |
| 267 | 279 | const width = comptime parser.specifier() catch |err| |
| ... | ... | @@ -284,8 +296,8 @@ pub const Placeholder = struct { |
| 284 | 296 | |
| 285 | 297 | return Placeholder{ |
| 286 | 298 | .specifier_arg = cacheString(specifier_arg[0..specifier_arg.len].*), |
| 287 | | .fill = fill, |
| 288 | | .alignment = alignment, |
| 299 | .fill = fill orelse default_fill_char, |
| 300 | .alignment = alignment orelse default_alignment, |
| 289 | 301 | .arg = arg, |
| 290 | 302 | .width = width, |
| 291 | 303 | .precision = precision, |
| ... | ... | @@ -2648,6 +2660,15 @@ test "sci float padding" { |
| 2648 | 2660 | try expectFmt("right-pad: 3.142e0****\n", "right-pad: {e:*<11.3}\n", .{number}); |
| 2649 | 2661 | } |
| 2650 | 2662 | |
| 2663 | test "padding.zero" { |
| 2664 | try expectFmt("zero-pad: '0042'", "zero-pad: '{:04}'", .{42}); |
| 2665 | try expectFmt("std-pad: ' 42'", "std-pad: '{:10}'", .{42}); |
| 2666 | try expectFmt("std-pad-1: '001'", "std-pad-1: '{:0>3}'", .{1}); |
| 2667 | try expectFmt("std-pad-2: '911'", "std-pad-2: '{:1<03}'", .{9}); |
| 2668 | try expectFmt("std-pad-3: ' 1'", "std-pad-3: '{:>03}'", .{1}); |
| 2669 | try expectFmt("center-pad: '515'", "center-pad: '{:5^03}'", .{1}); |
| 2670 | } |
| 2671 | |
| 2651 | 2672 | test "null" { |
| 2652 | 2673 | const inst = null; |
| 2653 | 2674 | try expectFmt("null", "{}", .{inst}); |