| ... | @@ -54,11 +54,7 @@ const extras = @import("extras"); | ... | @@ -54,11 +54,7 @@ const extras = @import("extras"); |
| 54 | /// A user type may be a `struct`, `vector`, `union` or `enum` type. | 54 | /// A user type may be a `struct`, `vector`, `union` or `enum` type. |
| 55 | /// | 55 | /// |
| 56 | /// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`. | 56 | /// To print literal curly braces, escape them by writing them twice, e.g. `{{` or `}}`. |
| 57 | pub fn format( | 57 | pub fn format(writer: anytype, comptime fmt: []const u8, args: anytype) !void { |
| 58 | writer: anytype, | | |
| 59 | comptime fmt: []const u8, | | |
| 60 | args: anytype, | | |
| 61 | ) !void { | | |
| 62 | const ArgsType = @TypeOf(args); | 58 | const ArgsType = @TypeOf(args); |
| 63 | const args_type_info = @typeInfo(ArgsType); | 59 | const args_type_info = @typeInfo(ArgsType); |
| 64 | if (args_type_info != .@"struct") { | 60 | if (args_type_info != .@"struct") { |
| ... | @@ -134,16 +130,14 @@ pub fn format( | ... | @@ -134,16 +130,14 @@ pub fn format( |
| 134 | const arg_pos = comptime switch (placeholder.arg) { | 130 | const arg_pos = comptime switch (placeholder.arg) { |
| 135 | .none => null, | 131 | .none => null, |
| 136 | .number => |pos| pos, | 132 | .number => |pos| pos, |
| 137 | .named => |arg_name| std.meta.fieldIndex(ArgsType, arg_name) orelse | 133 | .named => |arg_name| std.meta.fieldIndex(ArgsType, arg_name) orelse @compileError("no argument with name '" ++ arg_name ++ "'"), |
| 138 | @compileError("no argument with name '" ++ arg_name ++ "'"), | | |
| 139 | }; | 134 | }; |
| 140 | | 135 | |
| 141 | const width = switch (placeholder.width) { | 136 | const width = switch (placeholder.width) { |
| 142 | .none => null, | 137 | .none => null, |
| 143 | .number => |v| v, | 138 | .number => |v| v, |
| 144 | .named => |arg_name| blk: { | 139 | .named => |arg_name| blk: { |
| 145 | const arg_i = comptime std.meta.fieldIndex(ArgsType, arg_name) orelse | 140 | const arg_i = comptime std.meta.fieldIndex(ArgsType, arg_name) orelse @compileError("no argument with name '" ++ arg_name ++ "'"); |
| 146 | @compileError("no argument with name '" ++ arg_name ++ "'"); | | |
| 147 | _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); | 141 | _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); |
| 148 | break :blk @field(args, arg_name); | 142 | break :blk @field(args, arg_name); |
| 149 | }, | 143 | }, |
| ... | @@ -153,8 +147,7 @@ pub fn format( | ... | @@ -153,8 +147,7 @@ pub fn format( |
| 153 | .none => null, | 147 | .none => null, |
| 154 | .number => |v| v, | 148 | .number => |v| v, |
| 155 | .named => |arg_name| blk: { | 149 | .named => |arg_name| blk: { |
| 156 | const arg_i = comptime std.meta.fieldIndex(ArgsType, arg_name) orelse | 150 | const arg_i = comptime std.meta.fieldIndex(ArgsType, arg_name) orelse @compileError("no argument with name '" ++ arg_name ++ "'"); |
| 157 | @compileError("no argument with name '" ++ arg_name ++ "'"); | | |
| 158 | _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); | 151 | _ = comptime arg_state.nextArg(arg_i) orelse @compileError("too few arguments"); |
| 159 | break :blk @field(args, arg_name); | 152 | break :blk @field(args, arg_name); |
| 160 | }, | 153 | }, |
| ... | @@ -165,7 +158,7 @@ pub fn format( | ... | @@ -165,7 +158,7 @@ pub fn format( |
| 165 | try formatType( | 158 | try formatType( |
| 166 | @field(args, fields_info[arg_to_print].name), | 159 | @field(args, fields_info[arg_to_print].name), |
| 167 | placeholder.specifier_arg, | 160 | placeholder.specifier_arg, |
| 168 | FormatOptions{ | 161 | .{ |
| 169 | .fill = placeholder.fill, | 162 | .fill = placeholder.fill, |
| 170 | .alignment = placeholder.alignment, | 163 | .alignment = placeholder.alignment, |
| 171 | .width = width, | 164 | .width = width, |
| ... | @@ -244,8 +237,7 @@ pub const Placeholder = struct { | ... | @@ -244,8 +237,7 @@ pub const Placeholder = struct { |
| 244 | }; | 237 | }; |
| 245 | | 238 | |
| 246 | // Parse the positional argument number | 239 | // Parse the positional argument number |
| 247 | const arg = comptime parser.specifier() catch |err| | 240 | const arg = comptime parser.specifier() catch |err| @compileError(@errorName(err)); |
| 248 | @compileError(@errorName(err)); | | |
| 249 | | 241 | |
| 250 | // Parse the format specifier | 242 | // Parse the format specifier |
| 251 | const specifier_arg = comptime parser.until(':'); | 243 | const specifier_arg = comptime parser.until(':'); |
| ... | @@ -291,8 +283,7 @@ pub const Placeholder = struct { | ... | @@ -291,8 +283,7 @@ pub const Placeholder = struct { |
| 291 | } | 283 | } |
| 292 | | 284 | |
| 293 | // Parse the width parameter | 285 | // Parse the width parameter |
| 294 | const width = comptime parser.specifier() catch |err| | 286 | const width = comptime parser.specifier() catch |err| @compileError(@errorName(err)); |
| 295 | @compileError(@errorName(err)); | | |
| 296 | | 287 | |
| 297 | // Skip the dot, if present | 288 | // Skip the dot, if present |
| 298 | if (comptime parser.char()) |ch| { | 289 | if (comptime parser.char()) |ch| { |
| ... | @@ -302,8 +293,7 @@ pub const Placeholder = struct { | ... | @@ -302,8 +293,7 @@ pub const Placeholder = struct { |
| 302 | } | 293 | } |
| 303 | | 294 | |
| 304 | // Parse the precision parameter | 295 | // Parse the precision parameter |
| 305 | const precision = comptime parser.specifier() catch |err| | 296 | const precision = comptime parser.specifier() catch |err| @compileError(@errorName(err)); |
| 306 | @compileError(@errorName(err)); | | |
| 307 | | 297 | |
| 308 | if (comptime parser.char()) |ch| { | 298 | if (comptime parser.char()) |ch| { |
| 309 | @compileError("extraneous trailing character '" ++ std.unicode.utf8EncodeComptime(ch) ++ "'"); | 299 | @compileError("extraneous trailing character '" ++ std.unicode.utf8EncodeComptime(ch) ++ "'"); |
| ... | @@ -450,8 +440,7 @@ fn formatType(value: anytype, comptime fmt: []const u8, options: FormatOptions, | ... | @@ -450,8 +440,7 @@ fn formatType(value: anytype, comptime fmt: []const u8, options: FormatOptions, |
| 450 | return formatBuf(if (value) "true" else "false", options, writer); | 440 | return formatBuf(if (value) "true" else "false", options, writer); |
| 451 | }, | 441 | }, |
| 452 | .optional => { | 442 | .optional => { |
| 453 | if (actual_fmt.len == 0 or actual_fmt[0] != '?') | 443 | if (actual_fmt.len == 0 or actual_fmt[0] != '?') @compileError("cannot format optional without a specifier (i.e. {?} or {any})"); |
| 454 | @compileError("cannot format optional without a specifier (i.e. {?} or {any})"); | | |
| 455 | const remaining_fmt = comptime stripOptionalOrErrorUnionSpec(actual_fmt); | 444 | const remaining_fmt = comptime stripOptionalOrErrorUnionSpec(actual_fmt); |
| 456 | if (value) |payload| { | 445 | if (value) |payload| { |
| 457 | return formatType(payload, remaining_fmt, options, writer, max_depth); | 446 | return formatType(payload, remaining_fmt, options, writer, max_depth); |
| ... | @@ -460,8 +449,7 @@ fn formatType(value: anytype, comptime fmt: []const u8, options: FormatOptions, | ... | @@ -460,8 +449,7 @@ fn formatType(value: anytype, comptime fmt: []const u8, options: FormatOptions, |
| 460 | } | 449 | } |
| 461 | }, | 450 | }, |
| 462 | .error_union => { | 451 | .error_union => { |
| 463 | if (actual_fmt.len == 0 or actual_fmt[0] != '!') | 452 | if (actual_fmt.len == 0 or actual_fmt[0] != '!') @compileError("cannot format error union without a specifier (i.e. {!} or {any})"); |
| 464 | @compileError("cannot format error union without a specifier (i.e. {!} or {any})"); | | |
| 465 | const remaining_fmt = comptime stripOptionalOrErrorUnionSpec(actual_fmt); | 453 | const remaining_fmt = comptime stripOptionalOrErrorUnionSpec(actual_fmt); |
| 466 | if (value) |payload| { | 454 | if (value) |payload| { |
| 467 | return formatType(payload, remaining_fmt, options, writer, max_depth); | 455 | return formatType(payload, remaining_fmt, options, writer, max_depth); |
| ... | @@ -571,8 +559,9 @@ fn formatType(value: anytype, comptime fmt: []const u8, options: FormatOptions, | ... | @@ -571,8 +559,9 @@ fn formatType(value: anytype, comptime fmt: []const u8, options: FormatOptions, |
| 571 | invalidFmtError(fmt, value); | 559 | invalidFmtError(fmt, value); |
| 572 | }, | 560 | }, |
| 573 | .slice => { | 561 | .slice => { |
| 574 | if (actual_fmt.len == 0) | 562 | if (actual_fmt.len == 0) { |
| 575 | @compileError("cannot format slice without a specifier (i.e. {s} or {any})"); | 563 | @compileError("cannot format slice without a specifier (i.e. {s} or {any})"); |
| | 564 | } |
| 576 | if (max_depth == 0) { | 565 | if (max_depth == 0) { |
| 577 | return writer.writeAll("{ ... }"); | 566 | return writer.writeAll("{ ... }"); |
| 578 | } | 567 | } |
| ... | @@ -590,8 +579,9 @@ fn formatType(value: anytype, comptime fmt: []const u8, options: FormatOptions, | ... | @@ -590,8 +579,9 @@ fn formatType(value: anytype, comptime fmt: []const u8, options: FormatOptions, |
| 590 | }, | 579 | }, |
| 591 | }, | 580 | }, |
| 592 | .array => |info| { | 581 | .array => |info| { |
| 593 | if (actual_fmt.len == 0) | 582 | if (actual_fmt.len == 0) { |
| 594 | @compileError("cannot format array without a specifier (i.e. {s} or {any})"); | 583 | @compileError("cannot format array without a specifier (i.e. {s} or {any})"); |
| | 584 | } |
| 595 | if (max_depth == 0) { | 585 | if (max_depth == 0) { |
| 596 | return writer.writeAll("{ ... }"); | 586 | return writer.writeAll("{ ... }"); |
| 597 | } | 587 | } |
| ... | @@ -700,13 +690,7 @@ fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @TypeO | ... | @@ -700,13 +690,7 @@ fn formatAddress(value: anytype, options: FormatOptions, writer: anytype) @TypeO |
| 700 | @compileError("cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier"); | 690 | @compileError("cannot format non-pointer type " ++ @typeName(T) ++ " with * specifier"); |
| 701 | } | 691 | } |
| 702 | | 692 | |
| 703 | pub fn formatInt( | 693 | pub fn formatInt(value: anytype, base: u8, case: Case, options: FormatOptions, writer: anytype) !void { |
| 704 | value: anytype, | | |
| 705 | base: u8, | | |
| 706 | case: Case, | | |
| 707 | options: FormatOptions, | | |
| 708 | writer: anytype, | | |
| 709 | ) !void { | | |
| 710 | std.debug.assert(base >= 2); | 694 | std.debug.assert(base >= 2); |
| 711 | | 695 | |
| 712 | const int_value = if (@TypeOf(value) == comptime_int) blk: { | 696 | const int_value = if (@TypeOf(value) == comptime_int) blk: { |
| ... | @@ -768,12 +752,7 @@ pub fn formatInt( | ... | @@ -768,12 +752,7 @@ pub fn formatInt( |
| 768 | return formatBuf(buf[index..], options, writer); | 752 | return formatBuf(buf[index..], options, writer); |
| 769 | } | 753 | } |
| 770 | | 754 | |
| 771 | fn formatValue( | 755 | fn formatValue(value: anytype, comptime fmt: []const u8, options: FormatOptions, writer: anytype) !void { |
| 772 | value: anytype, | | |
| 773 | comptime fmt: []const u8, | | |
| 774 | options: FormatOptions, | | |
| 775 | writer: anytype, | | |
| 776 | ) !void { | | |
| 777 | const T = @TypeOf(value); | 756 | const T = @TypeOf(value); |
| 778 | switch (@typeInfo(T)) { | 757 | switch (@typeInfo(T)) { |
| 779 | // .float, .comptime_float => return formatFloatValue(value, fmt, options, writer), | 758 | // .float, .comptime_float => return formatFloatValue(value, fmt, options, writer), |
| ... | @@ -788,18 +767,13 @@ fn invalidFmtError(comptime fmt: []const u8, value: anytype) void { | ... | @@ -788,18 +767,13 @@ fn invalidFmtError(comptime fmt: []const u8, value: anytype) void { |
| 788 | @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); | 767 | @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'"); |
| 789 | } | 768 | } |
| 790 | | 769 | |
| 791 | fn formatBuf( | 770 | fn formatBuf(buf: []const u8, options: FormatOptions, writer: anytype) !void { |
| 792 | buf: []const u8, | | |
| 793 | options: FormatOptions, | | |
| 794 | writer: anytype, | | |
| 795 | ) !void { | | |
| 796 | if (options.width) |min_width| { | 771 | if (options.width) |min_width| { |
| 797 | // In case of error assume the buffer content is ASCII-encoded | 772 | // In case of error assume the buffer content is ASCII-encoded |
| 798 | const width = std.unicode.utf8CountCodepoints(buf) catch buf.len; | 773 | const width = std.unicode.utf8CountCodepoints(buf) catch buf.len; |
| 799 | const padding = if (width < min_width) min_width - width else 0; | 774 | const padding = if (width < min_width) min_width - width else 0; |
| 800 | | 775 | |
| 801 | if (padding == 0) | 776 | if (padding == 0) return writer.writeAll(buf); |
| 802 | return writer.writeAll(buf); | | |
| 803 | | 777 | |
| 804 | var fill_buffer: [4]u8 = undefined; | 778 | var fill_buffer: [4]u8 = undefined; |
| 805 | const fill_utf8 = if (std.unicode.utf8Encode(options.fill, &fill_buffer)) |len| | 779 | const fill_utf8 = if (std.unicode.utf8Encode(options.fill, &fill_buffer)) |len| |
| ... | @@ -863,14 +837,14 @@ const digits2_alphabet = blk: { | ... | @@ -863,14 +837,14 @@ const digits2_alphabet = blk: { |
| 863 | }; | 837 | }; |
| 864 | | 838 | |
| 865 | /// Converts values in the range [0, 100) to a base 10 string. | 839 | /// Converts values in the range [0, 100) to a base 10 string. |
| 866 | fn digits2(value: u8) [2]u8 { | 840 | pub fn digits2(value: u8) [2]u8 { |
| 867 | return digits2_alphabet[value * 2 ..][0..2].*; | 841 | return digits2_alphabet[value * 2 ..][0..2].*; |
| 868 | } | 842 | } |
| 869 | | 843 | |
| 870 | fn digitToChar(digit: u8, case: Case) u8 { | 844 | pub fn digitToChar(digit: u8, case: Case) u8 { |
| 871 | return switch (digit) { | 845 | return switch (digit) { |
| 872 | 0...9 => digit + '0', | 846 | 0...9 => digit + '0', |
| 873 | 10...35 => digit + ((if (case == .upper) @as(u8, 'A') else @as(u8, 'a')) - 10), | 847 | 10...35 => digit + (if (case == .upper) @as(u8, 'A') else @as(u8, 'a')) - 10, |
| 874 | else => unreachable, | 848 | else => unreachable, |
| 875 | }; | 849 | }; |
| 876 | } | 850 | } |