From 6f103922a8133ba11773e6ad9a52e26e1d99b3e7 Mon Sep 17 00:00:00 2001 From: Ivan Stepanov Date: Sun, 20 Jul 2025 21:40:56 +0200 Subject: Update to Zig 0.15.0-dev.1147 --- README.md | 87 ++++++++++++++++--------- build.zig | 12 ++-- build.zig.zon | 2 +- clap.zig | 128 ++++++++++++++++++------------------- clap/codepoint_counting_writer.zig | 68 ++++++++++---------- clap/streaming.zig | 6 +- example/help.zig | 8 ++- example/simple-ex.zig | 6 +- example/simple.zig | 6 +- example/streaming-clap.zig | 6 +- example/subcommands.zig | 13 ++-- example/usage.zig | 9 ++- 12 files changed, 201 insertions(+), 150 deletions(-) diff --git a/README.md b/README.md index fbc19e4..2e64264 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,9 @@ A simple and easy to use command line argument parser library for Zig. ## Installation Developers tend to either use -* The latest tagged release of Zig -* The latest build of Zigs master branch + +- The latest tagged release of Zig +- The latest build of Zigs master branch Depending on which developer you are, you need to run different `zig fetch` commands: @@ -29,22 +30,22 @@ exe.root_module.addImport("clap", clap.module("clap")); ## Features -* Short arguments `-a` - * Chaining `-abc` where `a` and `b` does not take values. - * Multiple specifications are tallied (e.g. `-v -v`). -* Long arguments `--long` -* Supports both passing values using spacing and `=` (`-a 100`, `-a=100`) - * Short args also support passing values with no spacing or `=` (`-a100`) - * This all works with chaining (`-ba 100`, `-ba=100`, `-ba100`) -* Supports options that can be specified multiple times (`-e 1 -e 2 -e 3`) -* Print help message from parameter specification. -* Parse help message to parameter specification. +- Short arguments `-a` + - Chaining `-abc` where `a` and `b` does not take values. + - Multiple specifications are tallied (e.g. `-v -v`). +- Long arguments `--long` +- Supports both passing values using spacing and `=` (`-a 100`, `-a=100`) + - Short args also support passing values with no spacing or `=` (`-a100`) + - This all works with chaining (`-ba 100`, `-ba=100`, `-ba100`) +- Supports options that can be specified multiple times (`-e 1 -e 2 -e 3`) +- Print help message from parameter specification. +- Parse help message to parameter specification. ## API Reference Automatically generated API Reference for the project can be found at -https://Hejsil.github.io/zig-clap. Note that Zig autodoc is in beta; the website -may be broken or incomplete. +https://Hejsil.github.io/zig-clap. Note that Zig autodoc is in beta; the website may be broken or +incomplete. ## Examples @@ -76,7 +77,10 @@ pub fn main() !void { .allocator = gpa.allocator(), }) catch |err| { // Report useful error and exit. - diag.report(std.io.getStdErr().writer(), err) catch {}; + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + diag.report(&stderr.interface, err) catch {}; + try stderr.interface.flush(); return err; }; defer res.deinit(); @@ -93,7 +97,6 @@ pub fn main() !void { const clap = @import("clap"); const std = @import("std"); - ``` The result will contain an `args` field and a `positionals` field. `args` will have one field for @@ -141,7 +144,10 @@ pub fn main() !void { // allowed. .assignment_separators = "=:", }) catch |err| { - diag.report(std.io.getStdErr().writer(), err) catch {}; + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + diag.report(&stderr.interface, err) catch {}; + try stderr.interface.flush(); return err; }; defer res.deinit(); @@ -160,7 +166,6 @@ pub fn main() !void { const clap = @import("clap"); const std = @import("std"); - ``` ### Subcommands @@ -212,7 +217,10 @@ pub fn main() !void { // not fully consumed. It can then be reused to parse the arguments for subcommands. .terminating_positional = 0, }) catch |err| { - diag.report(std.io.getStdErr().writer(), err) catch {}; + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + diag.report(&stderr.interface, err) catch {}; + try stderr.interface.flush(); return err; }; defer res.deinit(); @@ -248,8 +256,11 @@ fn mathMain(gpa: std.mem.Allocator, iter: *std.process.ArgIterator, main_args: M .diagnostic = &diag, .allocator = gpa, }) catch |err| { - diag.report(std.io.getStdErr().writer(), err) catch {}; - return err; + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + diag.report(&stderr.interface, err) catch {}; + try stderr.interface.flush(); + return err; // propagate error }; defer res.deinit(); @@ -265,7 +276,6 @@ fn mathMain(gpa: std.mem.Allocator, iter: *std.process.ArgIterator, main_args: M const clap = @import("clap"); const std = @import("std"); - ``` ### `streaming.Clap` @@ -310,7 +320,10 @@ pub fn main() !void { // Because we use a streaming parser, we have to consume each argument parsed individually. while (parser.next() catch |err| { // Report useful error and exit. - diag.report(std.io.getStdErr().writer(), err) catch {}; + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + diag.report(&stderr.interface, err) catch {}; + try stderr.interface.flush(); return err; }) |arg| { // arg.param will point to the parameter which matched the argument. @@ -329,10 +342,17 @@ pub fn main() !void { const clap = @import("clap"); const std = @import("std"); +``` +``` +$ zig-out/bin/streaming-clap --help --number=1 f=10 +Help! +--number = 1 +f=10 ``` -Currently, this parser is the only parser that allows an array of `Param` that is generated at runtime. +Currently, this parser is the only parser that allows an array of `Param` that is generated at +runtime. ### `help` @@ -360,13 +380,16 @@ pub fn main() !void { // where `Id` has a `description` and `value` method (`Param(Help)` is one such parameter). // The last argument contains options as to how `help` should print those parameters. Using // `.{}` means the default options. - if (res.args.help != 0) - return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); + if (res.args.help != 0) { + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + try clap.help(&stderr.interface, clap.Help, ¶ms, .{}); + return stderr.interface.flush(); + } } const clap = @import("clap"); const std = @import("std"); - ``` ``` @@ -402,17 +425,19 @@ pub fn main() !void { // `clap.usage` is a function that can print a simple help message. It can print any `Param` // where `Id` has a `value` method (`Param(Help)` is one such parameter). - if (res.args.help != 0) - return clap.usage(std.io.getStdErr().writer(), clap.Help, ¶ms); + if (res.args.help != 0) { + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + try clap.usage(&stderr.interface, clap.Help, ¶ms); + return stderr.interface.flush(); + } } const clap = @import("clap"); const std = @import("std"); - ``` ``` $ zig-out/bin/usage --help [-hv] [--value ] ``` - diff --git a/build.zig b/build.zig index 7a1fe95..02887e4 100644 --- a/build.zig +++ b/build.zig @@ -24,12 +24,16 @@ pub fn build(b: *std.Build) void { }) |example_name| { const example = b.addExecutable(.{ .name = example_name, - .root_source_file = b.path(b.fmt("example/{s}.zig", .{example_name})), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path(b.fmt("example/{s}.zig", .{example_name})), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "clap", .module = clap_mod }, + }, + }), }); const install_example = b.addInstallArtifact(example, .{}); - example.root_module.addImport("clap", clap_mod); example_step.dependOn(&example.step); example_step.dependOn(&install_example.step); } diff --git a/build.zig.zon b/build.zig.zon index 0fc81aa..883ed5e 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,7 +1,7 @@ .{ .name = .clap, .version = "0.10.0", - .minimum_zig_version = "0.14.0", + .minimum_zig_version = "0.15.0-dev.1147+69cf40da6", .fingerprint = 0x65f99e6f07a316a0, .paths = .{ "clap", diff --git a/clap.zig b/clap.zig index b666f56..3863087 100644 --- a/clap.zig +++ b/clap.zig @@ -542,7 +542,7 @@ pub const Diagnostic = struct { /// Default diagnostics reporter when all you want is English with no colors. /// Use this as a reference for implementing your own if needed. - pub fn report(diag: Diagnostic, stream: anytype, err: anyerror) !void { + pub fn report(diag: Diagnostic, stream: *std.Io.Writer, err: anyerror) !void { var longest = diag.name.longest(); if (longest.kind == .positional) longest.name = diag.arg; @@ -567,9 +567,9 @@ pub const Diagnostic = struct { fn testDiag(diag: Diagnostic, err: anyerror, expected: []const u8) !void { var buf: [1024]u8 = undefined; - var slice_stream = std.io.fixedBufferStream(&buf); - diag.report(slice_stream.writer(), err) catch unreachable; - try std.testing.expectEqualStrings(expected, slice_stream.getWritten()); + var writer = std.Io.Writer.fixed(&buf); + diag.report(&writer, err) catch unreachable; + try std.testing.expectEqualStrings(expected, writer.buffered()); } test "Diagnostic.report" { @@ -1263,9 +1263,9 @@ fn testErr( .diagnostic = &diag, }) catch |err| { var buf: [1024]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - diag.report(fbs.writer(), err) catch return error.TestFailed; - try std.testing.expectEqualStrings(expected, fbs.getWritten()); + var writer = std.Io.Writer.fixed(&buf); + diag.report(&writer, err) catch return error.TestFailed; + try std.testing.expectEqualStrings(expected, writer.buffered()); return; }; @@ -1366,7 +1366,7 @@ pub const HelpOptions = struct { /// The output can be constumized with the `opt` parameter. For default formatting `.{}` can /// be passed. pub fn help( - writer: anytype, + writer: *std.Io.Writer, comptime Id: type, params: []const Param(Id), opt: HelpOptions, @@ -1374,8 +1374,9 @@ pub fn help( const max_spacing = blk: { var res: usize = 0; for (params) |param| { - var cs = ccw.codepointCountingWriter(std.io.null_writer); - try printParam(cs.writer(), Id, param); + var discarding = std.Io.Writer.Discarding.init(&.{}); + var cs = ccw.CodepointCountingWriter.init(&discarding.writer); + try printParam(&cs.interface, Id, param); if (res < cs.codepoints_written) res = @intCast(cs.codepoints_written); } @@ -1390,16 +1391,15 @@ pub fn help( var first_parameter: bool = true; for (params) |param| { if (!first_parameter) - try writer.writeByteNTimes('\n', opt.spacing_between_parameters); + try writer.splatByteAll('\n', opt.spacing_between_parameters); first_parameter = false; - try writer.writeByteNTimes(' ', opt.indent); + try writer.splatByteAll(' ', opt.indent); - var cw = ccw.codepointCountingWriter(writer); - try printParam(cw.writer(), Id, param); + var cw = ccw.CodepointCountingWriter.init(writer); + try printParam(&cw.interface, Id, param); - const Writer = DescriptionWriter(@TypeOf(writer)); - var description_writer = Writer{ + var description_writer = DescriptionWriter{ .underlying_writer = writer, .indentation = description_indentation, .printed_chars = @intCast(cw.codepoints_written), @@ -1498,57 +1498,53 @@ pub fn help( } } -fn DescriptionWriter(comptime UnderlyingWriter: type) type { - return struct { - pub const WriteError = UnderlyingWriter.Error; - - underlying_writer: UnderlyingWriter, - - indentation: usize, - max_width: usize, - printed_chars: usize, - - pub fn writeWord(writer: *@This(), word: []const u8) !void { - std.debug.assert(word.len != 0); +const DescriptionWriter = struct { + underlying_writer: *std.Io.Writer, - var first_word = writer.printed_chars <= writer.indentation; - const chars_to_write = try std.unicode.utf8CountCodepoints(word) + @intFromBool(!first_word); - if (chars_to_write + writer.printed_chars > writer.max_width) { - // If the word does not fit on this line, then we insert a new line and print - // it on that line. The only exception to this is if this was the first word. - // If the first word does not fit on this line, then it will also not fit on the - // next one. In that case, all we can really do is just output the word. - if (!first_word) - try writer.newline(); + indentation: usize, + max_width: usize, + printed_chars: usize, - first_word = true; - } + pub fn writeWord(writer: *@This(), word: []const u8) !void { + std.debug.assert(word.len != 0); + var first_word = writer.printed_chars <= writer.indentation; + const chars_to_write = try std.unicode.utf8CountCodepoints(word) + @intFromBool(!first_word); + if (chars_to_write + writer.printed_chars > writer.max_width) { + // If the word does not fit on this line, then we insert a new line and print + // it on that line. The only exception to this is if this was the first word. + // If the first word does not fit on this line, then it will also not fit on the + // next one. In that case, all we can really do is just output the word. if (!first_word) - try writer.underlying_writer.writeAll(" "); + try writer.newline(); - try writer.ensureIndented(); - try writer.underlying_writer.writeAll(word); - writer.printed_chars += chars_to_write; + first_word = true; } - pub fn newline(writer: *@This()) !void { - try writer.underlying_writer.writeAll("\n"); - writer.printed_chars = 0; - } + if (!first_word) + try writer.underlying_writer.writeAll(" "); - fn ensureIndented(writer: *@This()) !void { - if (writer.printed_chars < writer.indentation) { - const to_indent = writer.indentation - writer.printed_chars; - try writer.underlying_writer.writeByteNTimes(' ', to_indent); - writer.printed_chars += to_indent; - } + try writer.ensureIndented(); + try writer.underlying_writer.writeAll(word); + writer.printed_chars += chars_to_write; + } + + pub fn newline(writer: *@This()) !void { + try writer.underlying_writer.writeAll("\n"); + writer.printed_chars = 0; + } + + fn ensureIndented(writer: *@This()) !void { + if (writer.printed_chars < writer.indentation) { + const to_indent = writer.indentation - writer.printed_chars; + try writer.underlying_writer.splatByteAll(' ', to_indent); + writer.printed_chars += to_indent; } - }; -} + } +}; fn printParam( - stream: anytype, + stream: *std.Io.Writer, comptime Id: type, param: Param(Id), ) !void { @@ -1583,9 +1579,9 @@ fn testHelp(opt: HelpOptions, str: []const u8) !void { defer std.testing.allocator.free(params); var buf: [2048]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - try help(fbs.writer(), Help, params, opt); - try std.testing.expectEqualStrings(str, fbs.getWritten()); + var writer = std.Io.Writer.fixed(&buf); + try help(&writer, Help, params, opt); + try std.testing.expectEqualStrings(str, writer.buffered()); } test "clap.help" { @@ -2015,9 +2011,9 @@ test "clap.help" { /// /// First all none value taking parameters, which have a short name are printed, then non /// positional parameters and finally the positional. -pub fn usage(stream: anytype, comptime Id: type, params: []const Param(Id)) !void { - var cos = ccw.codepointCountingWriter(stream); - const cs = cos.writer(); +pub fn usage(stream: *std.Io.Writer, comptime Id: type, params: []const Param(Id)) !void { + var cos = ccw.CodepointCountingWriter.init(stream); + const cs = &cos.interface; for (params) |param| { const name = param.names.short orelse continue; if (param.takes_value != .none) @@ -2060,7 +2056,7 @@ pub fn usage(stream: anytype, comptime Id: type, params: []const Param(Id)) !voi try cs.writeAll("..."); } - try cs.writeByte(']'); + try cs.writeAll("]"); } if (!has_positionals) @@ -2083,9 +2079,9 @@ pub fn usage(stream: anytype, comptime Id: type, params: []const Param(Id)) !voi fn testUsage(expected: []const u8, params: []const Param(Help)) !void { var buf: [1024]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - try usage(fbs.writer(), Help, params); - try std.testing.expectEqualStrings(expected, fbs.getWritten()); + var writer = std.Io.Writer.fixed(&buf); + try usage(&writer, Help, params); + try std.testing.expectEqualStrings(expected, writer.buffered()); } test "usage" { diff --git a/clap/codepoint_counting_writer.zig b/clap/codepoint_counting_writer.zig index c445c90..3518d48 100644 --- a/clap/codepoint_counting_writer.zig +++ b/clap/codepoint_counting_writer.zig @@ -1,30 +1,39 @@ /// A Writer that counts how many codepoints has been written to it. /// Expects valid UTF-8 input, and does not validate the input. -pub fn CodepointCountingWriter(comptime WriterType: type) type { - return struct { - codepoints_written: u64, - child_stream: WriterType, - - pub const Error = WriterType.Error || error{Utf8InvalidStartByte}; - pub const Writer = std.io.Writer(*Self, Error, write); +pub const CodepointCountingWriter = struct { + codepoints_written: u64 = 0, + child_stream: *std.Io.Writer, + interface: std.Io.Writer = .{ + .buffer = &.{}, + .vtable = &.{ .drain = drain }, + }, + + const Self = @This(); + + pub fn init(child_stream: *std.Io.Writer) Self { + return .{ + .child_stream = child_stream, + }; + } - const Self = @This(); + fn drain(w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize { + const self: *Self = @fieldParentPtr("interface", w); + var n_bytes_written: usize = 0; + var i: usize = 0; - pub fn write(self: *Self, bytes: []const u8) Error!usize { - const bytes_and_codepoints = try utf8CountCodepointsAllowTruncate(bytes); + while (i < data.len + splat - 1) : (i += 1) { + const chunk = data[@min(i, data.len)]; + const bytes_and_codepoints = utf8CountCodepointsAllowTruncate(chunk) catch return std.Io.Writer.Error.WriteFailed; // Might not be the full input, so the leftover bytes are written on the next call. - const bytes_to_write = bytes[0..bytes_and_codepoints.bytes]; + const bytes_to_write = chunk[0..bytes_and_codepoints.bytes]; const amt = try self.child_stream.write(bytes_to_write); + n_bytes_written += amt; const bytes_written = bytes_to_write[0..amt]; - self.codepoints_written += (try utf8CountCodepointsAllowTruncate(bytes_written)).codepoints; - return amt; - } - - pub fn writer(self: *Self) Writer { - return .{ .context = self }; + self.codepoints_written += (utf8CountCodepointsAllowTruncate(bytes_written) catch return std.Io.Writer.Error.WriteFailed).codepoints; } - }; -} + return n_bytes_written; + } +}; // Like `std.unicode.utf8CountCodepoints`, but on truncated input, it returns // the number of codepoints up to that point. @@ -58,44 +67,39 @@ fn utf8CountCodepointsAllowTruncate(s: []const u8) !struct { bytes: usize, codep return .{ .bytes = i, .codepoints = len }; } -pub fn codepointCountingWriter(child_stream: anytype) CodepointCountingWriter(@TypeOf(child_stream)) { - return .{ .codepoints_written = 0, .child_stream = child_stream }; -} - const testing = std.testing; test CodepointCountingWriter { - var counting_stream = codepointCountingWriter(std.io.null_writer); - const stream = counting_stream.writer(); + var discarding = std.Io.Writer.Discarding.init(&.{}); + var counting_stream = CodepointCountingWriter.init(&discarding.writer); const utf8_text = "blåhaj" ** 100; - stream.writeAll(utf8_text) catch unreachable; + counting_stream.interface.writeAll(utf8_text) catch unreachable; const expected_count = try std.unicode.utf8CountCodepoints(utf8_text); try testing.expectEqual(expected_count, counting_stream.codepoints_written); } test "handles partial UTF-8 writes" { var buf: [100]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - var counting_stream = codepointCountingWriter(fbs.writer()); - const stream = counting_stream.writer(); + var fbs = std.Io.Writer.fixed(&buf); + var counting_stream = CodepointCountingWriter.init(&fbs); const utf8_text = "ååå"; // `å` is represented as `\xC5\xA5`, write 1.5 `å`s. - var wc = try stream.write(utf8_text[0..3]); + var wc = try counting_stream.interface.write(utf8_text[0..3]); // One should have been written fully. try testing.expectEqual("å".len, wc); try testing.expectEqual(1, counting_stream.codepoints_written); // Write the rest, continuing from the reported number of bytes written. - wc = try stream.write(utf8_text[wc..]); + wc = try counting_stream.interface.write(utf8_text[wc..]); try testing.expectEqual(4, wc); try testing.expectEqual(3, counting_stream.codepoints_written); const expected_count = try std.unicode.utf8CountCodepoints(utf8_text); try testing.expectEqual(expected_count, counting_stream.codepoints_written); - try testing.expectEqualSlices(u8, utf8_text, fbs.getWritten()); + try testing.expectEqualSlices(u8, utf8_text, fbs.buffered()); } const std = @import("std"); diff --git a/clap/streaming.zig b/clap/streaming.zig index 4a687a2..fa5ec70 100644 --- a/clap/streaming.zig +++ b/clap/streaming.zig @@ -234,9 +234,9 @@ fn expectError( while (parser.next() catch |err| { var buf: [1024]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - diag.report(fbs.writer(), err) catch return error.TestFailed; - try std.testing.expectEqualStrings(expected, fbs.getWritten()); + var fbs = std.Io.Writer.fixed(&buf); + diag.report(&fbs, err) catch return error.TestFailed; + try std.testing.expectEqualStrings(expected, fbs.buffered()); return; }) |_| {} diff --git a/example/help.zig b/example/help.zig index b80ee35..676a56a 100644 --- a/example/help.zig +++ b/example/help.zig @@ -17,8 +17,12 @@ pub fn main() !void { // where `Id` has a `description` and `value` method (`Param(Help)` is one such parameter). // The last argument contains options as to how `help` should print those parameters. Using // `.{}` means the default options. - if (res.args.help != 0) - return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); + if (res.args.help != 0) { + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + try clap.help(&stderr.interface, clap.Help, ¶ms, .{}); + return stderr.interface.flush(); + } } const clap = @import("clap"); diff --git a/example/simple-ex.zig b/example/simple-ex.zig index 22f657f..a993868 100644 --- a/example/simple-ex.zig +++ b/example/simple-ex.zig @@ -31,7 +31,11 @@ pub fn main() !void { // allowed. .assignment_separators = "=:", }) catch |err| { - diag.report(std.io.getStdErr().writer(), err) catch {}; + // Report useful error and exit. + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + try diag.report(&stderr.interface, err); + try stderr.interface.flush(); return err; }; defer res.deinit(); diff --git a/example/simple.zig b/example/simple.zig index 2b7bf0a..ca6bd75 100644 --- a/example/simple.zig +++ b/example/simple.zig @@ -21,8 +21,10 @@ pub fn main() !void { .allocator = gpa.allocator(), }) catch |err| { // Report useful error and exit. - diag.report(std.io.getStdErr().writer(), err) catch {}; - return err; + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + try diag.report(&stderr.interface, err); + return stderr.interface.flush(); }; defer res.deinit(); diff --git a/example/streaming-clap.zig b/example/streaming-clap.zig index 054c401..d60167c 100644 --- a/example/streaming-clap.zig +++ b/example/streaming-clap.zig @@ -34,8 +34,10 @@ pub fn main() !void { // Because we use a streaming parser, we have to consume each argument parsed individually. while (parser.next() catch |err| { // Report useful error and exit. - diag.report(std.io.getStdErr().writer(), err) catch {}; - return err; + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + try diag.report(&stderr.interface, err); + return stderr.interface.flush(); }) |arg| { // arg.param will point to the parameter which matched the argument. switch (arg.param.id) { diff --git a/example/subcommands.zig b/example/subcommands.zig index 8223f31..644e371 100644 --- a/example/subcommands.zig +++ b/example/subcommands.zig @@ -41,8 +41,10 @@ pub fn main() !void { // not fully consumed. It can then be reused to parse the arguments for subcommands. .terminating_positional = 0, }) catch |err| { - diag.report(std.io.getStdErr().writer(), err) catch {}; - return err; + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + try diag.report(&stderr.interface, err); + return stderr.interface.flush(); }; defer res.deinit(); @@ -77,8 +79,11 @@ fn mathMain(gpa: std.mem.Allocator, iter: *std.process.ArgIterator, main_args: M .diagnostic = &diag, .allocator = gpa, }) catch |err| { - diag.report(std.io.getStdErr().writer(), err) catch {}; - return err; + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + try diag.report(&stderr.interface, err); + try stderr.interface.flush(); + return err; // propagate error }; defer res.deinit(); diff --git a/example/usage.zig b/example/usage.zig index 59ac84a..8bd25b7 100644 --- a/example/usage.zig +++ b/example/usage.zig @@ -16,8 +16,13 @@ pub fn main() !void { // `clap.usage` is a function that can print a simple help message. It can print any `Param` // where `Id` has a `value` method (`Param(Help)` is one such parameter). - if (res.args.help != 0) - return clap.usage(std.io.getStdErr().writer(), clap.Help, ¶ms); + if (res.args.help != 0) { + var buf: [1024]u8 = undefined; + var stderr = std.fs.File.stderr().writer(&buf); + clap.usage(&stderr.interface, clap.Help, ¶ms) catch {}; + try stderr.interface.flush(); + return; + } } const clap = @import("clap"); -- cgit v1.2.3