Skip to content

Commit

Permalink
Pass all config structs by value
Browse files Browse the repository at this point in the history
  • Loading branch information
sam701 committed Dec 30, 2023
1 parent 9da61d3 commit 6e318bb
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 78 deletions.
3 changes: 1 addition & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub fn build(b: *std.Build) void {
});
simple.addModule("zig-cli", module);
b.installArtifact(simple);
b.default_step.dependOn(&simple.step);

const short = b.addExecutable(.{
.name = "short",
Expand All @@ -40,7 +41,5 @@ pub fn build(b: *std.Build) void {
});
short.addModule("zig-cli", module);
b.installArtifact(short);

// b.default_step.dependOn(&simple.step);
b.default_step.dependOn(&short.step);
}
4 changes: 2 additions & 2 deletions examples/short.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ pub fn main() !void {
.command = cli.Command{
.name = "short",
.options = &.{
&cli.Option{
.{
.long_name = "port",
.help = "port to bind to",
.required = true,
.value_ref = r.mkRef(&config.port),
},
&cli.Option{
.{
.long_name = "host",
.help = "host to listen on",
.value_ref = r.mkRef(&config.host),
Expand Down
40 changes: 20 additions & 20 deletions examples/simple.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,6 @@ var config = struct {
pub fn main() anyerror!void {
var r = try cli.AppRunner.init(std.heap.page_allocator);

const arg1 = cli.PositionalArg{
.name = "ARG1",
.help = "arg1 help",
.value_ref = r.mkRef(&config.arg1),
};

const arg2 = cli.PositionalArg{
.name = "ARG2",
.help = "multiple arg2 help",
.value_ref = r.mkRef(&config.arg2),
};

const sub2 = cli.Command{
.name = "sub2",
.target = cli.CommandTarget{
Expand All @@ -45,8 +33,20 @@ pub fn main() anyerror!void {
.target = cli.CommandTarget{
.action = cli.CommandAction{
.positional_args = cli.PositionalArgs{
.args = &.{ &arg1, &arg2 },
.first_optional_arg = &arg2,
.required = &.{
cli.PositionalArg{
.name = "ARG1",
.help = "arg1 help",
.value_ref = r.mkRef(&config.arg1),
},
},
.optional = &.{
cli.PositionalArg{
.name = "ARG2",
.help = "multiple arg2 help",
.value_ref = r.mkRef(&config.arg2),
},
},
},
.exec = run_sub3,
},
Expand All @@ -60,7 +60,7 @@ pub fn main() anyerror!void {
},
.target = cli.CommandTarget{
.subcommands = &.{
&cli.Command{
cli.Command{
.name = "sub1",
.description = cli.Description{
.one_line = "another awesome command",
Expand All @@ -71,33 +71,33 @@ pub fn main() anyerror!void {
,
},
.options = &.{
&cli.Option{
.{
.long_name = "ip",
.help = "this is the IP address",
.short_alias = 'i',
.value_ref = r.mkRef(&config.ip),
.required = true,
.value_name = "IP",
},
&cli.Option{
.{
.long_name = "int",
.help = "this is an int",
.value_ref = r.mkRef(&config.int),
},
&cli.Option{
.{
.long_name = "bool",
.short_alias = 'b',
.help = "this is a bool",
.value_ref = r.mkRef(&config.bool),
},
&cli.Option{
.{
.long_name = "float",
.help = "this is a float",
.value_ref = r.mkRef(&config.float),
},
},
.target = cli.CommandTarget{
.subcommands = &.{ &sub2, &sub3 },
.subcommands = &.{ sub2, sub3 },
},
},
},
Expand Down
58 changes: 58 additions & 0 deletions src/PositionalArgsHelper.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const command = @import("command.zig");

inner: *const command.PositionalArgs,

const Self = @This();

pub fn len(self: *const Self) usize {
var cnt: usize = 0;
if (self.inner.required) |x| {
cnt += x.len;
}
if (self.inner.optional) |x| {
cnt += x.len;
}
return cnt;
}

pub fn at(self: *const Self, ix: usize) *const command.PositionalArg {
var ix2 = ix;
if (self.inner.required) |x| {
if (ix < x.len) {
return &x[ix];
} else {
ix2 -= x.len;
}
}
if (self.inner.optional) |x| {
return &x[ix2];
}

unreachable;
}

pub fn iterator(self: *const Self) Iterator {
return Iterator.init(self);
}

const Iterator = struct {
helper: *const Self,
len: usize,
index: usize,

fn init(helper: *const Self) Iterator {
return .{
.helper = helper,
.len = helper.len(),
.index = 0,
};
}

pub fn next(self: *Iterator) ?*const command.PositionalArg {
if (self.index >= self.len) return null;

const x = self.helper.at(self.index);
self.index += 1;
return x;
}
};
10 changes: 4 additions & 6 deletions src/command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub const HelpConfig = struct {
pub const Command = struct {
name: []const u8,
description: ?Description = null,
options: ?[]const *const Option = null,
options: ?[]const Option = null,
target: CommandTarget,
};

Expand All @@ -40,7 +40,7 @@ pub const Description = struct {
};

pub const CommandTarget = union(enum) {
subcommands: []const *const Command,
subcommands: []const Command,
action: CommandAction,
};

Expand All @@ -62,10 +62,8 @@ pub const Option = struct {
};

pub const PositionalArgs = struct {
args: []const *const PositionalArg,

/// If not set, all positional arguments are considered as required.
first_optional_arg: ?*const PositionalArg = null,
required: ?[]const PositionalArg = null,
optional: ?[]const PositionalArg = null,
};

pub const PositionalArg = struct {
Expand Down
41 changes: 24 additions & 17 deletions src/help.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const command = @import("command.zig");
const Printer = @import("Printer.zig");
const value_ref = @import("value_ref.zig");
const GlobalOptions = @import("GlobalOptions.zig");
const PositionalArgsHelper = @import("PositionalArgsHelper.zig");

const color_clear = "0";

Expand Down Expand Up @@ -56,22 +57,25 @@ const HelpPrinter = struct {
switch (cmd.target) {
.action => |act| {
if (act.positional_args) |pargs| {
var closeOpt = false;
for (pargs.args) |parg| {
self.printer.write(" ");
if (pargs.first_optional_arg) |opt| {
if (opt == parg) {
self.printer.write("[");
closeOpt = true;
if (pargs.required) |req| {
for (req) |*parg| {
self.printer.write(" ");
self.printer.format("<{s}>", .{parg.name});
if (parg.value_ref.value_type == value_ref.ValueType.multi) {
self.printer.write("...");
}
}
self.printer.format("<{s}>", .{parg.name});
if (parg.value_ref.value_type == value_ref.ValueType.multi) {
self.printer.write("...");
}
}
if (closeOpt) {
self.printer.write("]");
if (pargs.optional) |opt| {
for (opt) |*parg| {
self.printer.write(" ");
self.printer.write("[");
self.printer.format("<{s}>", .{parg.name});
if (parg.value_ref.value_type == value_ref.ValueType.multi) {
self.printer.write("...");
}
self.printer.write("]");
}
}
}
},
Expand All @@ -89,13 +93,16 @@ const HelpPrinter = struct {

switch (cmd.target) {
.action => |act| {
if (act.positional_args) |pargs| {
if (act.positional_args) |*pargs| {
self.printer.printInColor(self.help_config.color_section, "\nARGUMENTS:\n");
var max_arg_width: usize = 0;
for (pargs.args) |parg| {
const arg_h = PositionalArgsHelper{ .inner = pargs };
var it = arg_h.iterator();
while (it.next()) |parg| {
max_arg_width = @max(max_arg_width, parg.name.len);
}
for (pargs.args) |parg| {
it.index = 0;
while (it.next()) |parg| {
self.printer.write(" ");
self.printer.printInColor(self.help_config.color_option, parg.name);
self.printer.printSpaces(max_arg_width - parg.name.len + 3);
Expand Down Expand Up @@ -144,7 +151,7 @@ const HelpPrinter = struct {
}
option_column_width += 3;
if (cmd.options) |option_list| {
for (option_list) |option| {
for (option_list) |*option| {
self.printOption(option, option_column_width);
}
}
Expand Down
31 changes: 15 additions & 16 deletions src/parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const value_parser = @import("value_parser.zig");
const str_true = value_parser.str_true;
const str_false = value_parser.str_false;
const GlobalOptions = @import("GlobalOptions.zig");
const PositionalArgsHelper = @import("PositionalArgsHelper.zig");

pub const ParseResult = command.ExecFn;

Expand Down Expand Up @@ -67,7 +68,7 @@ pub fn Parser(comptime Iterator: type) type {
fn finalize(self: *Self) !ParseResult {
for (self.command_path.items) |cmd| {
if (cmd.options) |options| {
for (options) |opt| {
for (options) |*opt| {
try self.set_option_value_from_envvar(opt);
try opt.value_ref.finalize(self.alloc);

Expand All @@ -78,17 +79,14 @@ pub fn Parser(comptime Iterator: type) type {
}
switch (cmd.target) {
.action => |act| {
if (act.positional_args) |pargs| {
var optional = false;
for (pargs.args) |parg| {
if (act.positional_args) |*pargs| {
const argh = PositionalArgsHelper{ .inner = pargs };
var it = argh.iterator();
const required_args_no = if (pargs.required) |req| req.len else 0;
while (it.next()) |parg| {
try parg.value_ref.finalize(self.alloc);

if (pargs.first_optional_arg) |first_opt| {
if (parg == first_opt) {
optional = true;
}
}
if (!optional and parg.value_ref.element_count == 0) {
if (it.index <= required_args_no and parg.value_ref.element_count == 0) {
self.fail("missing required positional argument '{s}'", .{parg.name});
}
}
Expand Down Expand Up @@ -116,12 +114,13 @@ pub fn Parser(comptime Iterator: type) type {
self.fail("command '{s}' cannot have positional arguments", .{cmd.name});
},
.action => |act| {
if (act.positional_args) |posArgs| {
if (self.position_argument_ix >= posArgs.args.len) {
if (act.positional_args) |*posArgs| {
var posH = PositionalArgsHelper{ .inner = posArgs };
if (self.position_argument_ix >= posH.len()) {
self.fail("unexpected positional argument '{s}'", .{arg});
}

const posArg = posArgs.args[self.position_argument_ix];
const posArg = posH.at(self.position_argument_ix);
var posArgRef = posArg.value_ref;
posArgRef.put(arg, self.alloc) catch |err| {
self.fail("positional argument ({s}): cannot parse '{s}' as {s}: {s}", .{ posArg.name, arg, posArgRef.value_data.type_name, @errorName(err) });
Expand Down Expand Up @@ -187,7 +186,7 @@ pub fn Parser(comptime Iterator: type) type {
const cmd = self.current_command();
switch (cmd.target) {
.subcommands => |cmds| {
for (cmds) |sc| {
for (cmds) |*sc| {
if (std.mem.eql(u8, sc.name, some_name)) {
try self.command_path.append(sc);
return false;
Expand Down Expand Up @@ -282,7 +281,7 @@ pub fn Parser(comptime Iterator: type) type {
for (0..self.command_path.items.len) |ix| {
const cmd = self.command_path.items[self.command_path.items.len - ix - 1];
if (cmd.options) |option_list| {
for (option_list) |option| {
for (option_list) |*option| {
if (std.mem.eql(u8, option.long_name, option_name)) {
return option;
}
Expand All @@ -303,7 +302,7 @@ pub fn Parser(comptime Iterator: type) type {
return self.global_options.option_show_help;
}
if (cmd.options) |option_list| {
for (option_list) |option| {
for (option_list) |*option| {
if (option.short_alias) |alias| {
if (alias == option_alias) {
return option;
Expand Down
Loading

0 comments on commit 6e318bb

Please sign in to comment.