| author | |
| committer | |
| log | d09c1aef02788bcea0a3e3b0f7507c4f3e823295 |
| tree | d2b6e2868baddb5f0e7fe79b326d31a900d09c1c |
| parent | 7cef4755c33ca77137fb7e4d83146e27b5703685 |
serves an ErrorBundle as a configuration_failed message instead of
configuration message upon connection. at this point the protocol enters
an invalid state and the process will need to be restarted in order to
reconfigure. in the future, build server should outlive
reconfigurations.
closes #363894 files changed, 54 insertions(+), 68 deletions(-)
lib/compiler/Maker.zig+8-2| ... | @@ -740,6 +740,9 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -740,6 +740,9 @@ pub fn main(init: process.Init.Minimal) !void { |
| 740 | var configure_source_files: Cache.Manifest.SelfContainedFiles = .empty; | 740 | var configure_source_files: Cache.Manifest.SelfContainedFiles = .empty; |
| 741 | defer configure_source_files.deinit(gpa); | 741 | defer configure_source_files.deinit(gpa); |
| 742 | 742 | ||
| 743 | var configure_error_bundle: std.zig.ErrorBundle = .empty; | ||
| 744 | defer configure_error_bundle.deinit(gpa); | ||
| 745 | |||
| 743 | // If this fails, we can still start the server and wait for user | 746 | // If this fails, we can still start the server and wait for user |
| 744 | // to request a rebuild. If it returns error.FailedButCacheIntact | 747 | // to request a rebuild. If it returns error.FailedButCacheIntact |
| 745 | // we can even still do file system watching and automatically | 748 | // we can even still do file system watching and automatically |
| ... | @@ -762,6 +765,7 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -762,6 +765,7 @@ pub fn main(init: process.Init.Minimal) !void { |
| 762 | .print_configuration = print_configuration, | 765 | .print_configuration = print_configuration, |
| 763 | .forks = forks.items, | 766 | .forks = forks.items, |
| 764 | .src_files = &configure_source_files, | 767 | .src_files = &configure_source_files, |
| 768 | .error_bundle = if (protocol_server != null) &configure_error_bundle else null, | ||
| 765 | })) |scanned_config| { | 769 | })) |scanned_config| { |
| 766 | if (help_menu) { | 770 | if (help_menu) { |
| 767 | scanned_config.printUsage(&graph, initStdoutWriter(io)) catch |err| switch (err) { | 771 | scanned_config.printUsage(&graph, initStdoutWriter(io)) catch |err| switch (err) { |
| ... | @@ -1048,8 +1052,8 @@ pub fn main(init: process.Init.Minimal) !void { | ... | @@ -1048,8 +1052,8 @@ pub fn main(init: process.Init.Minimal) !void { |
| 1048 | _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; | 1052 | _ = io.lockStderr(&.{}, graph.stderr_mode) catch {}; |
| 1049 | process.exit(1); | 1053 | process.exit(1); |
| 1050 | } | 1054 | } |
| 1051 | if (protocol_server != null) { | 1055 | if (protocol_server) |s| { |
| 1052 | fatal("(zig build system) TODO send error messages to client when build.zig compilation fails", .{}); | 1056 | try s.serveErrorBundle(.bsp_configuration_failed, configure_error_bundle); |
| 1053 | } | 1057 | } |
| 1054 | if (watch_flag and can_fs_watch) { | 1058 | if (watch_flag and can_fs_watch) { |
| 1055 | fatal("(zig build system) TODO set up fs watching even when build.zig compilation fails", .{}); | 1059 | fatal("(zig build system) TODO set up fs watching even when build.zig compilation fails", .{}); |
| ... | @@ -1087,6 +1091,7 @@ const ConfigureOptions = struct { | ... | @@ -1087,6 +1091,7 @@ const ConfigureOptions = struct { |
| 1087 | print_configuration: PrintConfiguration, | 1091 | print_configuration: PrintConfiguration, |
| 1088 | forks: []Fork, | 1092 | forks: []Fork, |
| 1089 | src_files: *Cache.Manifest.SelfContainedFiles, | 1093 | src_files: *Cache.Manifest.SelfContainedFiles, |
| 1094 | error_bundle: ?*std.zig.ErrorBundle = null, | ||
| 1090 | }; | 1095 | }; |
| 1091 | 1096 | ||
| 1092 | fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { | 1097 | fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { |
| ... | @@ -1471,6 +1476,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { | ... | @@ -1471,6 +1476,7 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig { |
| 1471 | .arch_os_abi = target_arch_os_abi, | 1476 | .arch_os_abi = target_arch_os_abi, |
| 1472 | .progress_node = compile_prog_node, | 1477 | .progress_node = compile_prog_node, |
| 1473 | .skip_log_cmdline_on_compile_errors = !graph.verbose, | 1478 | .skip_log_cmdline_on_compile_errors = !graph.verbose, |
| 1479 | .error_bundle = options.error_bundle, | ||
| 1474 | })) |r| r.path else |err| return err; | 1480 | })) |r| r.path else |err| return err; |
| 1475 | defer gpa.free(configure_exe_path.sub_path); | 1481 | defer gpa.free(configure_exe_path.sub_path); |
| 1476 | 1482 |
lib/std/zig.zig+17-7| ... | @@ -1713,6 +1713,9 @@ pub const BuildExeSubprocessOptions = struct { | ... | @@ -1713,6 +1713,9 @@ pub const BuildExeSubprocessOptions = struct { |
| 1713 | cpu_features: ?[]const u8 = null, | 1713 | cpu_features: ?[]const u8 = null, |
| 1714 | progress_node: std.Progress.Node = .none, | 1714 | progress_node: std.Progress.Node = .none, |
| 1715 | skip_log_cmdline_on_compile_errors: bool = false, | 1715 | skip_log_cmdline_on_compile_errors: bool = false, |
| 1716 | /// If this is provided, compilation errors are sent here. Otherwise, they are printed to stderr. | ||
| 1717 | /// Must be an initialized `ErrorBundle`; if it is updated then it is cleared first. | ||
| 1718 | error_bundle: ?*ErrorBundle = null, | ||
| 1716 | }; | 1719 | }; |
| 1717 | 1720 | ||
| 1718 | pub const BuildExeSubprocessError = error{ | 1721 | pub const BuildExeSubprocessError = error{ |
| ... | @@ -1781,8 +1784,10 @@ pub fn buildExeSubprocess( | ... | @@ -1781,8 +1784,10 @@ pub fn buildExeSubprocess( |
| 1781 | var result: ?Cache.Path = null; | 1784 | var result: ?Cache.Path = null; |
| 1782 | defer if (result) |r| gpa.free(r.sub_path); | 1785 | defer if (result) |r| gpa.free(r.sub_path); |
| 1783 | 1786 | ||
| 1784 | var result_error_bundle: ErrorBundle = .empty; | 1787 | var default_error_bundle: ErrorBundle = .empty; |
| 1785 | defer result_error_bundle.deinit(gpa); | 1788 | defer default_error_bundle.deinit(gpa); |
| 1789 | |||
| 1790 | const error_bundle = options.error_bundle orelse &default_error_bundle; | ||
| 1786 | 1791 | ||
| 1787 | var received_fs_inputs = false; | 1792 | var received_fs_inputs = false; |
| 1788 | var cache_hit = false; | 1793 | var cache_hit = false; |
| ... | @@ -1816,8 +1821,8 @@ pub fn buildExeSubprocess( | ... | @@ -1816,8 +1821,8 @@ pub fn buildExeSubprocess( |
| 1816 | } | 1821 | } |
| 1817 | }, | 1822 | }, |
| 1818 | .error_bundle => { | 1823 | .error_bundle => { |
| 1819 | result_error_bundle.deinit(gpa); | 1824 | error_bundle.deinit(gpa); |
| 1820 | result_error_bundle = Server.allocErrorBundle(gpa, body) catch |err| switch (err) { | 1825 | error_bundle.* = Server.allocErrorBundle(gpa, body) catch |err| switch (err) { |
| 1821 | error.EndOfStream => break, | 1826 | error.EndOfStream => break, |
| 1822 | else => |e| return e, | 1827 | else => |e| return e, |
| 1823 | }; | 1828 | }; |
| ... | @@ -1884,8 +1889,8 @@ pub fn buildExeSubprocess( | ... | @@ -1884,8 +1889,8 @@ pub fn buildExeSubprocess( |
| 1884 | return error.AlreadyReported; | 1889 | return error.AlreadyReported; |
| 1885 | } | 1890 | } |
| 1886 | 1891 | ||
| 1887 | if (result_error_bundle.errorMessageCount() > 0) { | 1892 | if (default_error_bundle.errorMessageCount() > 0) { |
| 1888 | result_error_bundle.renderToStderr(io, .{}, .auto) catch |err| switch (err) { | 1893 | default_error_bundle.renderToStderr(io, .{}, .auto) catch |err| switch (err) { |
| 1889 | error.Canceled => |e| return e, | 1894 | error.Canceled => |e| return e, |
| 1890 | else => |e| { | 1895 | else => |e| { |
| 1891 | log.err("failed rendering error bundle: {t}", .{e}); | 1896 | log.err("failed rendering error bundle: {t}", .{e}); |
| ... | @@ -1893,12 +1898,17 @@ pub fn buildExeSubprocess( | ... | @@ -1893,12 +1898,17 @@ pub fn buildExeSubprocess( |
| 1893 | }, | 1898 | }, |
| 1894 | }; | 1899 | }; |
| 1895 | if (!options.skip_log_cmdline_on_compile_errors) log.err("command reported {d} compilation errors: {f}", .{ | 1900 | if (!options.skip_log_cmdline_on_compile_errors) log.err("command reported {d} compilation errors: {f}", .{ |
| 1896 | result_error_bundle.errorMessageCount(), cmd, | 1901 | default_error_bundle.errorMessageCount(), cmd, |
| 1897 | }); | 1902 | }); |
| 1898 | if (received_fs_inputs) return error.FailedButCacheIntact; | 1903 | if (received_fs_inputs) return error.FailedButCacheIntact; |
| 1899 | return error.AlreadyReported; | 1904 | return error.AlreadyReported; |
| 1900 | } | 1905 | } |
| 1901 | 1906 | ||
| 1907 | if (error_bundle.errorMessageCount() > 0) { | ||
| 1908 | if (received_fs_inputs) return error.FailedButCacheIntact; | ||
| 1909 | return error.AlreadyReported; | ||
| 1910 | } | ||
| 1911 | |||
| 1902 | const base_path = result orelse { | 1912 | const base_path = result orelse { |
| 1903 | log.err("command failed to report result: {f}", .{cmd}); | 1913 | log.err("command failed to report result: {f}", .{cmd}); |
| 1904 | return error.AlreadyReported; | 1914 | return error.AlreadyReported; |
lib/std/zig/Server.zig+8-22| ... | @@ -83,6 +83,10 @@ pub const Message = struct { | ... | @@ -83,6 +83,10 @@ pub const Message = struct { |
| 83 | /// Body is a cwd relative path to the configuration file. | 83 | /// Body is a cwd relative path to the configuration file. |
| 84 | /// This message only applies to the build system protocol. | 84 | /// This message only applies to the build system protocol. |
| 85 | bsp_configuration, | 85 | bsp_configuration, |
| 86 | /// build.zig itself failed to build from source. | ||
| 87 | /// Body is an `ErrorBundle` | ||
| 88 | /// This message only applies to the build system protocol. | ||
| 89 | bsp_configuration_failed, | ||
| 86 | /// Does not have a body. | 90 | /// Does not have a body. |
| 87 | /// This message only applies to the build system protocol. | 91 | /// This message only applies to the build system protocol. |
| 88 | bsp_build_started, | 92 | bsp_build_started, |
| ... | @@ -307,7 +311,7 @@ pub fn serveTestResults(s: *Server, msg: OutMessage.TestResults) !void { | ... | @@ -307,7 +311,7 @@ pub fn serveTestResults(s: *Server, msg: OutMessage.TestResults) !void { |
| 307 | try s.out.flush(); | 311 | try s.out.flush(); |
| 308 | } | 312 | } |
| 309 | 313 | ||
| 310 | pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { | 314 | pub fn serveErrorBundle(s: *Server, tag: Message.Tag, error_bundle: std.zig.ErrorBundle) !void { |
| 311 | const eb_hdr: OutMessage.ErrorBundle = .{ | 315 | const eb_hdr: OutMessage.ErrorBundle = .{ |
| 312 | .extra_len = @intCast(error_bundle.extra.len), | 316 | .extra_len = @intCast(error_bundle.extra.len), |
| 313 | .string_bytes_len = @intCast(error_bundle.string_bytes.len), | 317 | .string_bytes_len = @intCast(error_bundle.string_bytes.len), |
| ... | @@ -315,7 +319,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { | ... | @@ -315,7 +319,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { |
| 315 | const bytes_len = @sizeOf(OutMessage.ErrorBundle) + | 319 | const bytes_len = @sizeOf(OutMessage.ErrorBundle) + |
| 316 | 4 * error_bundle.extra.len + error_bundle.string_bytes.len; | 320 | 4 * error_bundle.extra.len + error_bundle.string_bytes.len; |
| 317 | try s.serveMessageHeader(.{ | 321 | try s.serveMessageHeader(.{ |
| 318 | .tag = .error_bundle, | 322 | .tag = tag, |
| 319 | .bytes_len = @intCast(bytes_len), | 323 | .bytes_len = @intCast(bytes_len), |
| 320 | }); | 324 | }); |
| 321 | try s.out.writeStruct(eb_hdr, .little); | 325 | try s.out.writeStruct(eb_hdr, .little); |
| ... | @@ -327,31 +331,13 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { | ... | @@ -327,31 +331,13 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void { |
| 327 | pub fn allocErrorBundle(gpa: Allocator, body: []const u8) error{ OutOfMemory, EndOfStream }!std.zig.ErrorBundle { | 331 | pub fn allocErrorBundle(gpa: Allocator, body: []const u8) error{ OutOfMemory, EndOfStream }!std.zig.ErrorBundle { |
| 328 | var r: Reader = .fixed(body); | 332 | var r: Reader = .fixed(body); |
| 329 | const hdr = r.takeStruct(OutMessage.ErrorBundle, .little) catch |err| switch (err) { | 333 | const hdr = r.takeStruct(OutMessage.ErrorBundle, .little) catch |err| switch (err) { |
| 330 | error.EndOfStream => |e| return e, | ||
| 331 | error.ReadFailed => unreachable, | 334 | error.ReadFailed => unreachable, |
| 332 | }; | ||
| 333 | |||
| 334 | var eb: std.zig.ErrorBundle = .{ | ||
| 335 | .string_bytes = &.{}, | ||
| 336 | .extra = &.{}, | ||
| 337 | }; | ||
| 338 | errdefer eb.deinit(gpa); | ||
| 339 | |||
| 340 | const extra = try gpa.alloc(u32, hdr.extra_len); | ||
| 341 | eb.extra = extra; | ||
| 342 | const string_bytes = try gpa.alloc(u8, hdr.string_bytes_len); | ||
| 343 | eb.string_bytes = string_bytes; | ||
| 344 | |||
| 345 | r.readSliceEndian(u32, extra, .little) catch |err| switch (err) { | ||
| 346 | error.EndOfStream => |e| return e, | 335 | error.EndOfStream => |e| return e, |
| 347 | error.ReadFailed => unreachable, | ||
| 348 | }; | 336 | }; |
| 349 | r.readSliceAll(string_bytes) catch |err| switch (err) { | 337 | return std.zig.ErrorBundle.readAlloc(&r, gpa, hdr.extra_len, hdr.string_bytes_len) catch |err| switch (err) { |
| 350 | error.EndOfStream => |e| return e, | ||
| 351 | error.ReadFailed => unreachable, | 338 | error.ReadFailed => unreachable, |
| 339 | else => |e| return e, | ||
| 352 | }; | 340 | }; |
| 353 | |||
| 354 | return eb; | ||
| 355 | } | 341 | } |
| 356 | 342 | ||
| 357 | pub const TestMetadata = struct { | 343 | pub const TestMetadata = struct { |
tools/bsp.zig+21-37| ... | @@ -7,6 +7,7 @@ const Configuration = std.Build.Configuration; | ... | @@ -7,6 +7,7 @@ const Configuration = std.Build.Configuration; |
| 7 | const Client = std.zig.Client; | 7 | const Client = std.zig.Client; |
| 8 | const Server = std.zig.Server; | 8 | const Server = std.zig.Server; |
| 9 | const log = std.log.scoped(.bsp); | 9 | const log = std.log.scoped(.bsp); |
| 10 | const panic = std.debug.panic; | ||
| 10 | 11 | ||
| 11 | pub fn main(init: std.process.Init) !void { | 12 | pub fn main(init: std.process.Init) !void { |
| 12 | const io = init.io; | 13 | const io = init.io; |
| ... | @@ -32,7 +33,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -32,7 +33,7 @@ pub fn main(init: std.process.Init) !void { |
| 32 | .stdin = .pipe, | 33 | .stdin = .pipe, |
| 33 | .stdout = .pipe, | 34 | .stdout = .pipe, |
| 34 | .stderr = .pipe, | 35 | .stderr = .pipe, |
| 35 | }) catch |err| std.debug.panic("failed to spawn process: {}", .{err}); | 36 | }) catch |err| panic("failed to spawn process: {}", .{err}); |
| 36 | errdefer child_process.kill(io); | 37 | errdefer child_process.kill(io); |
| 37 | 38 | ||
| 38 | var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; | 39 | var multi_reader_buffer: Io.File.MultiReader.Buffer(2) = undefined; |
| ... | @@ -66,10 +67,10 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -66,10 +67,10 @@ pub fn main(init: std.process.Init) !void { |
| 66 | }, | 67 | }, |
| 67 | }; | 68 | }; |
| 68 | const body = client_stdout.take(header.bytes_len) catch unreachable; | 69 | const body = client_stdout.take(header.bytes_len) catch unreachable; |
| 69 | log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len }); | 70 | log.debug("received {} ({d} bytes)", .{ header.tag, body.len }); |
| 70 | 71 | ||
| 71 | if (header.tag != .bsp_handshake) { | 72 | if (header.tag != .bsp_handshake) { |
| 72 | log.err("received unexpected message: {f}", .{fmtEnum(header.tag)}); | 73 | log.err("received unexpected message: {}", .{header.tag}); |
| 73 | return error.UnexpectedMessage; | 74 | return error.UnexpectedMessage; |
| 74 | } | 75 | } |
| 75 | 76 | ||
| ... | @@ -94,17 +95,26 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -94,17 +95,26 @@ pub fn main(init: std.process.Init) !void { |
| 94 | const body = client_stdout.take(header.bytes_len) catch unreachable; | 95 | const body = client_stdout.take(header.bytes_len) catch unreachable; |
| 95 | log.debug("received {t} ({d} bytes)", .{ header.tag, body.len }); | 96 | log.debug("received {t} ({d} bytes)", .{ header.tag, body.len }); |
| 96 | 97 | ||
| 97 | if (header.tag != .bsp_configuration) { | 98 | switch (header.tag) { |
| 98 | log.err("received unexpected message: {f}", .{fmtEnum(header.tag)}); | 99 | .bsp_configuration => {}, |
| 99 | return error.UnexpectedMessage; | 100 | .bsp_configuration_failed => { |
| 101 | var eb = try Server.allocErrorBundle(gpa, body); | ||
| 102 | defer eb.deinit(gpa); | ||
| 103 | try eb.renderToStderr(io, .{}, .auto); | ||
| 104 | panic("configuration failed", .{}); | ||
| 105 | }, | ||
| 106 | else => { | ||
| 107 | log.err("received unexpected message: {}", .{header.tag}); | ||
| 108 | return error.UnexpectedMessage; | ||
| 109 | }, | ||
| 100 | } | 110 | } |
| 101 | 111 | ||
| 102 | const configuration_path = body; | 112 | const configuration_path = body; |
| 103 | var file = Io.Dir.cwd().openFile(io, configuration_path, .{}) catch |err| | 113 | var file = Io.Dir.cwd().openFile(io, configuration_path, .{}) catch |err| |
| 104 | std.debug.panic("failed to open configuration file {q}: {t}", .{ configuration_path, err }); | 114 | panic("failed to open configuration file {q}: {t}", .{ configuration_path, err }); |
| 105 | defer file.close(io); | 115 | defer file.close(io); |
| 106 | break :configuration Configuration.loadFile(conf_arena, io, file) catch |err| | 116 | break :configuration Configuration.loadFile(conf_arena, io, file) catch |err| |
| 107 | std.debug.panic("failed to load configuration file {q}: {t}", .{ configuration_path, err }); | 117 | panic("failed to load configuration file {q}: {t}", .{ configuration_path, err }); |
| 108 | }; | 118 | }; |
| 109 | const c = &configuration; | 119 | const c = &configuration; |
| 110 | 120 | ||
| ... | @@ -157,7 +167,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -157,7 +167,7 @@ pub fn main(init: std.process.Init) !void { |
| 157 | if (std.fmt.parseInt(u32, arg, 10)) |i| | 167 | if (std.fmt.parseInt(u32, arg, 10)) |i| |
| 158 | @fromBackingInt(i) | 168 | @fromBackingInt(i) |
| 159 | else |_| | 169 | else |_| |
| 160 | top_level_steps.get(arg) orelse std.debug.panic("unexpected step name or index", .{}); | 170 | top_level_steps.get(arg) orelse panic("unexpected step name or index", .{}); |
| 161 | try steps.append(gpa, step); | 171 | try steps.append(gpa, step); |
| 162 | } | 172 | } |
| 163 | } | 173 | } |
| ... | @@ -178,7 +188,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -178,7 +188,7 @@ pub fn main(init: std.process.Init) !void { |
| 178 | }, | 188 | }, |
| 179 | }; | 189 | }; |
| 180 | const body = client_stdout.take(header.bytes_len) catch unreachable; | 190 | const body = client_stdout.take(header.bytes_len) catch unreachable; |
| 181 | log.debug("received {f} ({d} bytes)", .{ fmtEnum(header.tag), body.len }); | 191 | log.debug("received {} ({d} bytes)", .{ header.tag, body.len }); |
| 182 | 192 | ||
| 183 | switch (header.tag) { | 193 | switch (header.tag) { |
| 184 | .bsp_build_started => {}, | 194 | .bsp_build_started => {}, |
| ... | @@ -214,7 +224,7 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -214,7 +224,7 @@ pub fn main(init: std.process.Init) !void { |
| 214 | } | 224 | } |
| 215 | }, | 225 | }, |
| 216 | .bsp_configuration => @panic("TODO"), | 226 | .bsp_configuration => @panic("TODO"), |
| 217 | else => std.debug.panic("received unexpected message: {f}", .{fmtEnum(header.tag)}), | 227 | else => panic("received unexpected message: {}", .{header.tag}), |
| 218 | } | 228 | } |
| 219 | } | 229 | } |
| 220 | continue; | 230 | continue; |
| ... | @@ -242,29 +252,3 @@ pub fn main(init: std.process.Init) !void { | ... | @@ -242,29 +252,3 @@ pub fn main(init: std.process.Init) !void { |
| 242 | log.err("maker {f}", .{term}); | 252 | log.err("maker {f}", .{term}); |
| 243 | } | 253 | } |
| 244 | } | 254 | } |
| 245 | |||
| 246 | const FormatEnum = union(enum) { | ||
| 247 | named: []const u8, | ||
| 248 | unnamed: usize, | ||
| 249 | |||
| 250 | pub fn format( | ||
| 251 | e: FormatEnum, | ||
| 252 | writer: *std.Io.Writer, | ||
| 253 | ) std.Io.Writer.Error!void { | ||
| 254 | switch (e) { | ||
| 255 | .named => |name| { | ||
| 256 | try writer.writeByte('.'); | ||
| 257 | try writer.writeAll(name); | ||
| 258 | }, | ||
| 259 | .unnamed => |number| try writer.print("0x{x}", .{number}), | ||
| 260 | } | ||
| 261 | } | ||
| 262 | }; | ||
| 263 | |||
| 264 | fn fmtEnum(e: anytype) FormatEnum { | ||
| 265 | if (std.enums.tagName(@TypeOf(e), e)) |name| { | ||
| 266 | return .{ .named = name }; | ||
| 267 | } else { | ||
| 268 | return .{ .unnamed = @backingInt(e) }; | ||
| 269 | } | ||
| 270 | } |