Skip to content

Commit e8a9c58

Browse files
committed
Use std.Io
1 parent f9a516d commit e8a9c58

8 files changed

Lines changed: 56 additions & 54 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ var config = struct {
3333
}{};
3434
3535
pub fn main() !void {
36-
var r = try cli.AppRunner.init(std.heap.page_allocator);
36+
var threaded = std.Io.Threaded.init(std.heap.page_allocator, .{});
37+
defer threaded.deinit();
38+
var r = try cli.AppRunner.init(threaded.io(), std.heap.page_allocator);
3739
3840
// Create an App with a command named "short" that takes host and port options.
3941
const app = cli.App{

examples/short.zig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ var config = struct {
88
}{};
99

1010
pub fn main() !void {
11-
var r = try cli.AppRunner.init(std.heap.page_allocator);
11+
var threaded = std.Io.Threaded.init(std.heap.page_allocator, .{});
12+
defer threaded.deinit();
13+
var r = try cli.AppRunner.init(threaded.io(), std.heap.page_allocator);
1214

1315
// Create an App with a command named "short" that takes host and port options.
1416
const app = cli.App{

examples/simple.zig

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ fn sub3(r: *cli.AppRunner) !cli.Command {
5252
};
5353
}
5454

55-
fn parseArgs() cli.AppRunner.Error!cli.ExecFn {
55+
fn parseArgs(io: std.Io) cli.AppRunner.Error!cli.ExecFn {
5656
// This allocator will be used to allocate config.ip and config.arg2.
57-
var r = try cli.AppRunner.init(allocator);
57+
var r = try cli.AppRunner.init(io, allocator);
5858

5959
const sub2 = cli.Command{
6060
.name = "sub2",
@@ -130,7 +130,10 @@ fn parseArgs() cli.AppRunner.Error!cli.ExecFn {
130130
}
131131

132132
pub fn main() anyerror!void {
133-
const action = try parseArgs();
133+
var threaded = std.Io.Threaded.init(std.heap.page_allocator, .{});
134+
defer threaded.deinit();
135+
136+
const action = try parseArgs(threaded.io());
134137
const r = action();
135138
freeConfig();
136139
return r;

src/Printer.zig

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ use_color: bool,
88

99
const color_clear = "0";
1010

11-
pub fn init(writer: *std.Io.Writer, use_color: bool) Self {
11+
pub fn init(writer: *std.Io.File.Writer) Self {
1212
return .{
13-
.writer = writer,
14-
.use_color = use_color,
13+
.writer = &writer.interface,
14+
.use_color = writer.file.isTty(writer.io) catch unreachable,
1515
};
1616
}
1717

src/app_runner.zig

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@ const command = @import("command.zig");
1111
const help = @import("./help.zig");
1212

1313
pub const AppRunner = struct {
14-
// This arena and its allocator is intended to be used only for the value references
15-
// that must be freed after the parsing is finished.
16-
// For everything else the original allocator.
14+
// Arena allocator for temporary data during parsing (ValueRefs, argument slices, etc.)
15+
// that is freed immediately after parsing completes.
16+
// The original allocator is used for data that outlives the parsing phase.
1717
arena: ArenaAllocator,
1818
orig_allocator: Allocator,
19+
io: std.Io,
1920

2021
const Self = @This();
21-
pub fn init(alloc: Allocator) !Self {
22+
pub fn init(io: std.Io, alloc: Allocator) !Self {
2223
return .{
2324
.arena = ArenaAllocator.init(alloc),
2425
.orig_allocator = alloc,
26+
.io = io,
2527
};
2628
}
2729

@@ -57,19 +59,22 @@ pub const AppRunner = struct {
5759
const iter = try std.process.argsWithAllocator(self.arena.allocator());
5860

5961
// Here we pass the child allocator because any values allocated on the client behalf may not be freed.
60-
var cr = try Parser(std.process.ArgIterator).init(app, iter, self.orig_allocator);
62+
var cr = try Parser(std.process.ArgIterator).init(app, iter, self.io, self.orig_allocator);
6163
defer cr.deinit();
6264

6365
if (cr.parse()) |action| {
6466
self.deinit();
6567
return action;
6668
} else |err| {
67-
processError(err, cr.error_data orelse unreachable, app);
69+
var buffer: [4096]u8 = undefined;
70+
var w = std.Io.File.stderr().writer(self.io, &buffer);
71+
var printer = Printer.init(&w);
72+
processError(&printer, err, cr.error_data orelse unreachable, app);
6873
if (app.help_config.print_help_on_error) {
69-
_ = std.fs.File.stdout().write("\n") catch unreachable;
70-
try help.print_command_help(app, try cr.command_path.toOwnedSlice(self.orig_allocator), cr.global_options);
74+
printer.printNewLine();
75+
try help.print_command_help(&printer, app, try cr.command_path.toOwnedSlice(self.orig_allocator), cr.global_options);
7176
}
72-
std.posix.exit(1);
77+
std.process.exit(1);
7378
}
7479
}
7580

@@ -83,21 +88,22 @@ pub const AppRunner = struct {
8388
}
8489
};
8590

86-
fn processError(err: parser.ParseError, err_data: parser.ErrorData, app: *const App) void {
91+
fn processError(p: *Printer, err: parser.ParseError, err_data: parser.ErrorData, app: *const App) void {
8792
switch (err) {
88-
error.UnknownOption => printError(app, "unknown option '--{s}'", .{err_data.provided_string}),
89-
error.UnknownOptionAlias => printError(app, "unknown option alias '-{c}'", .{err_data.option_alias}),
90-
error.UnknownSubcommand => printError(app, "unknown subcommand '{s}'", .{err_data.provided_string}),
91-
error.MissingRequiredOption => printError(app, "missing required option '--{s}'", .{err_data.entity_name}),
92-
error.MissingRequiredPositionalArgument => printError(app, "missing required positional argument '{s}'", .{err_data.entity_name}),
93-
error.MissingSubcommand => printError(app, "command '{s}' requires subcommand", .{err_data.entity_name}),
94-
error.MissingOptionValue => printError(app, "option ('--{s}') requires value", .{err_data.entity_name}),
95-
error.UnexpectedPositionalArgument => printError(app, "unexpected positional argument '{s}'", .{err_data.provided_string}),
96-
error.CommandDoesNotHavePositionalArguments => printError(app, "command '{s}' does not have positional arguments", .{err_data.entity_name}),
93+
error.UnknownOption => printError(p, app, "unknown option '--{s}'", .{err_data.provided_string}),
94+
error.UnknownOptionAlias => printError(p, app, "unknown option alias '-{c}'", .{err_data.option_alias}),
95+
error.UnknownSubcommand => printError(p, app, "unknown subcommand '{s}'", .{err_data.provided_string}),
96+
error.MissingRequiredOption => printError(p, app, "missing required option '--{s}'", .{err_data.entity_name}),
97+
error.MissingRequiredPositionalArgument => printError(p, app, "missing required positional argument '{s}'", .{err_data.entity_name}),
98+
error.MissingSubcommand => printError(p, app, "command '{s}' requires subcommand", .{err_data.entity_name}),
99+
error.MissingOptionValue => printError(p, app, "option ('--{s}') requires value", .{err_data.entity_name}),
100+
error.UnexpectedPositionalArgument => printError(p, app, "unexpected positional argument '{s}'", .{err_data.provided_string}),
101+
error.CommandDoesNotHavePositionalArguments => printError(p, app, "command '{s}' does not have positional arguments", .{err_data.entity_name}),
97102
error.InvalidValue => {
98103
const iv = err_data.invalid_value;
99104
if (iv.envvar) |ev| {
100105
printError(
106+
p,
101107
app,
102108
"failed to parse '{s}' (read from envvar {s}) as the value for option '--{s}' which is of type {s}",
103109
.{ iv.provided_string, ev, iv.entity_name, iv.value_type },
@@ -106,27 +112,18 @@ fn processError(err: parser.ParseError, err_data: parser.ErrorData, app: *const
106112
const et = if (iv.entity_type == .option) "option" else "positional argument";
107113
const px = if (iv.entity_type == .option) "--" else "";
108114
printError(
115+
p,
109116
app,
110117
"failed to parse '{s}' as the value for {s} '{s}{s}' which is of type {s}",
111118
.{ iv.provided_string, et, px, iv.entity_name, iv.value_type },
112119
);
113120
}
114121
},
115-
error.OutOfMemory => printError(app, "out of memory", .{}),
122+
error.OutOfMemory => printError(p, app, "out of memory", .{}),
116123
}
117124
}
118125

119-
pub fn printError(app: *const App, comptime fmt: []const u8, args: anytype) void {
120-
var buf: [4096]u8 = undefined;
121-
var stderr = std.fs.File.stderr();
122-
var w = stderr.writer(&buf);
123-
const use_color = switch (app.help_config.color_usage) {
124-
.always => true,
125-
.never => false,
126-
.auto => std.posix.isatty(stderr.handle),
127-
};
128-
var p = Printer.init(&w.interface, use_color);
129-
126+
pub fn printError(p: *Printer, app: *const App, comptime fmt: []const u8, args: anytype) void {
130127
p.printInColor(app.help_config.color_error, "ERROR");
131128
p.format(": ", .{});
132129
p.format(fmt, args);

src/help.zig

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,20 @@ const Allocator = std.mem.Allocator;
1010
const color_clear = "0";
1111

1212
pub fn print_command_help(
13+
printer: *Printer,
1314
app: *const command.App,
1415
command_path: []const *const command.Command,
1516
global_options: *const GlobalOptions,
1617
) !void {
17-
const stdout: std.fs.File = .stdout();
1818
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
1919
const allocator = gpa.allocator();
2020
var arena = std.heap.ArenaAllocator.init(allocator);
2121
defer arena.deinit();
2222

23-
const use_color = switch (global_options.color_usage) {
24-
.always => true,
25-
.never => false,
26-
.auto => std.posix.isatty(stdout.handle),
27-
};
28-
29-
var buf: [4096]u8 = undefined;
30-
var w = stdout.writer(&buf);
31-
var printer = Printer.init(&w.interface, use_color);
3223
defer printer.flush();
3324
var help_printer = HelpPrinter{
3425
.app = app,
35-
.printer = &printer,
26+
.printer = printer,
3627
.global_options = global_options,
3728
.allocator = arena.allocator(),
3829
};

src/parser.zig

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,13 @@ pub fn Parser(comptime Iterator: type) type {
5959
global_options: *GlobalOptions,
6060
error_data: ?ErrorData = null,
6161

62-
pub fn init(app: *const command.App, it: Iterator, alloc: Allocator) !Self {
62+
io: std.Io,
63+
64+
pub fn init(app: *const command.App, it: Iterator, io: std.Io, alloc: Allocator) !Self {
6365
return .{
6466
.orig_allocator = alloc,
6567
.arena = ArenaAllocator.init(alloc),
68+
.io = io,
6669
.arg_iterator = it,
6770
.app = app,
6871
.command_path = try std.ArrayList(*const command.Command).initCapacity(alloc, 16),
@@ -255,8 +258,11 @@ pub fn Parser(comptime Iterator: type) type {
255258
};
256259

257260
if (opt == self.global_options.option_show_help) {
258-
try help.print_command_help(self.app, try self.command_path.toOwnedSlice(self.orig_allocator), self.global_options);
259-
std.posix.exit(0);
261+
var buf: [4096]u8 = undefined;
262+
var writer = std.Io.File.stdout().writer(self.io, &buf);
263+
var printer = Printer.init(&writer);
264+
try help.print_command_help(&printer, self.app, try self.command_path.toOwnedSlice(self.orig_allocator), self.global_options);
265+
std.process.exit(0);
260266
}
261267

262268
if (opt.value_ref.value_data.is_bool) {

src/tests.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ const StringSliceIterator = struct {
2222
};
2323

2424
fn runner() AppRunner {
25-
return AppRunner.init(alloc) catch unreachable;
25+
return AppRunner.init(std.testing.io, alloc) catch unreachable;
2626
}
2727

2828
fn run(app: *const command.App, items: []const []const u8) !void {
2929
var parser = try Parser(StringSliceIterator).init(
3030
app,
3131
StringSliceIterator{ .items = items },
32+
std.testing.io,
3233
alloc,
3334
);
3435
defer parser.deinit();

0 commit comments

Comments
 (0)