Skip to content

Commit

Permalink
Merge branch 'dynamic-settings-generator'
Browse files Browse the repository at this point in the history
  • Loading branch information
PrajwalCH committed Oct 25, 2022
2 parents 10ad491 + b32dbe6 commit 77a3729
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 91 deletions.
2 changes: 1 addition & 1 deletion docs/data.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions examples/git.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ pub fn main() anyerror!void {

var cmd_pull = app.createCommand("pull", "Fetch from remote branch and merge it to local");
try cmd_pull.takesSingleValue("REMOTE");
cmd_pull.argRequired(true);
cmd_pull.applySetting(.arg_required);

var cmd_push = app.createCommand("push", "Update the remote branch");
try cmd_push.takesSingleValue("REMOTE");
try cmd_push.takesSingleValue("BRANCH_NAME");
cmd_push.argRequired(true);
cmd_push.applySetting(.arg_required);

try git.addSubcommand(app.createCommand("init", "Create an empty Git repository or reinitialize an existing one"));
try git.addSubcommand(cmd_commit);
Expand Down
2 changes: 1 addition & 1 deletion examples/touch.zig
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub fn main() anyerror!void {
var touch = app.rootCommand();

try touch.takesSingleValue("FILE_NAME");
touch.argRequired(true);
touch.applySetting(.arg_required);

try touch.addArg(flag.boolean("no-create", 'c', "Do not create any files"));
try touch.addArg(flag.boolean("version", 'v', "Display app version"));
Expand Down
61 changes: 26 additions & 35 deletions src/Arg.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
const Arg = @This();
const std = @import("std");
const ArgsContext = @import("parser/ArgsContext.zig");
const MakeSettings = @import("settings.zig").MakeSettings;

const Settings = struct {
takes_value: bool = false,
takes_multiple_values: bool = false,
allow_empty_value: bool = false,
};
const Settings = MakeSettings(&[_][]const u8{
"takes_value",
"takes_multiple_values",
"allow_empty_value",
});

name: []const u8,
short_name: ?u8,
Expand Down Expand Up @@ -57,56 +58,34 @@ pub fn setDescription(self: *Arg, description: []const u8) void {
}

/// Sets the minimum number of values required to provide for an argument.
/// Implicitly sets the `Arg.takesValue(true)`
/// Implicitly applies the `.takes_value` setting
pub fn minValues(self: *Arg, num: usize) void {
if (num >= 1) {
self.min_values = num;
self.takesValue(true);
self.applySetting(.takes_value);
}
}

/// Sets the maximum number of values an argument can take.
/// Implicitly sets the `Arg.takesValue(true)`
/// Implicitly applies the `.takes_value` setting
pub fn maxValues(self: *Arg, num: usize) void {
self.max_values = num;
self.takesValue(true);
self.applySetting(.takes_value);
}

/// Sets the allowed values for an argument.
/// Value outside of allowed values will be consider as error.
/// Implicitly sets the `Arg.takesValue(true)`
/// Implicitly applies the `.takes_value` setting
pub fn allowedValues(self: *Arg, values: []const []const u8) void {
self.allowed_values = values;
self.takesValue(true);
self.applySetting(.takes_value);
}

/// Sets separator between the values of an argument.
/// Implicitly sets the `Arg.takesValue(true)`
/// Implicitly applies the `.takes_value` setting
pub fn valuesDelimiter(self: *Arg, delimiter: []const u8) void {
self.values_delimiter = delimiter;
self.takesValue(true);
}

/// Specifies that an argument will takes a value
pub fn takesValue(self: *Arg, b: bool) void {
self.settings.takes_value = b;
}

/// Specifies that the argument will takes an unknown number of values.
/// You can use `Arg.maxValues(n)` to limit the number of values.
///
/// Note:
/// Values will continue to be consume until one of the following condition wiil satisfies:
/// 1. If another flag is found
/// 2. If parser reaches the end of raw argument
/// 3. If parser reaches the maximum number of values. Requires to explicitly set `Arg.maxValues(n)`
pub fn takesMultipleValues(self: *Arg, b: bool) void {
self.settings.takes_multiple_values = b;
}

/// Specifies wether an argument can take a empty value or not
pub fn allowedEmptyValue(self: *Arg, b: bool) void {
self.settings.allow_empty_value = b;
self.applySetting(.takes_value);
}

pub fn verifyValueInAllowedValues(self: *const Arg, value_to_check: []const u8) bool {
Expand All @@ -120,6 +99,18 @@ pub fn verifyValueInAllowedValues(self: *const Arg, value_to_check: []const u8)
}
}

pub fn applySetting(self: *Arg, option: Settings.Options) void {
return self.settings.apply(option);
}

pub fn removeSetting(self: *Arg, option: Settings.Options) void {
return self.settings.remove(option);
}

pub fn isSettingApplied(self: *const Arg, option: Settings.Options) bool {
return self.settings.isApplied(option);
}

test "emit methods docs" {
std.testing.refAllDecls(@This());
}
43 changes: 20 additions & 23 deletions src/Command.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@ const Command = @This();
const std = @import("std");
const Arg = @import("Arg.zig");
const Help = @import("Help.zig");
const MakeSettings = @import("settings.zig").MakeSettings;

const mem = std.mem;
const ArrayList = std.ArrayListUnmanaged;
const Allocator = mem.Allocator;

const Setting = struct {
takes_value: bool = false,
arg_required: bool = false,
subcommand_required: bool = false,
};
const Settings = MakeSettings(&[_][]const u8{
"takes_value",
"arg_required",
"subcommand_required",
});

allocator: Allocator,
name: []const u8,
description: ?[]const u8 = null,
args: ArrayList(Arg) = .{},
options: ArrayList(Arg) = .{},
subcommands: ArrayList(Command) = .{},
setting: Setting = .{},
settings: Settings = .{},

/// Creates a new instance of it
pub fn new(allocator: Allocator, name: []const u8) Command {
Expand Down Expand Up @@ -81,22 +81,7 @@ pub fn takesNValues(self: *Command, arg_name: []const u8, n: usize) !void {
if (n > 1) arg.valuesDelimiter(",");

try self.addArg(arg);
self.takesValue(true);
}

/// Specifies that the command takes value. Default to 'false`
pub fn takesValue(self: *Command, b: bool) void {
self.setting.takes_value = b;
}

/// Specifies that argument is required to provide. Default to `false`
pub fn argRequired(self: *Command, boolean: bool) void {
self.setting.arg_required = boolean;
}

/// Specifies that sub-command is required to provide. Default to `false`
pub fn subcommandRequired(self: *Command, boolean: bool) void {
self.setting.subcommand_required = boolean;
self.applySetting(.takes_value);
}

pub fn countArgs(self: *const Command) usize {
Expand Down Expand Up @@ -145,6 +130,18 @@ pub fn findSubcommand(self: *const Command, provided_subcmd: []const u8) ?*const
return null;
}

pub fn applySetting(self: *Command, option: Settings.Options) void {
return self.settings.apply(option);
}

pub fn removeSetting(self: *Command, option: Settings.Options) void {
return self.settings.remove(option);
}

pub fn isSettingApplied(self: *const Command, option: Settings.Options) bool {
return self.settings.isApplied(option);
}

pub fn help(self: *const Command) Help {
return Help.init(self);
}
Expand Down
27 changes: 14 additions & 13 deletions src/Help.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ const Help = @This();

const std = @import("std");
const Command = @import("Command.zig");
const MakeSettings = @import("settings.zig").MakeSettings;
const Braces = std.meta.Tuple(&[2]type{ u8, u8 });

pub const Options = struct {
include_args: bool = false,
include_subcmds: bool = false,
include_flags: bool = false,
};
pub const Options = MakeSettings(&[_][]const u8{
"include_args",
"include_subcmds",
"include_flags",
});

cmd: *const Command,
options: Options = .{},
Expand Down Expand Up @@ -45,7 +46,7 @@ pub fn writeAll(self: *Help) !void {
try self.writeCommands(writer);
try self.writeOptions(writer);

if (self.options.include_subcmds) {
if (self.options.isApplied(.include_subcmds)) {
try writeNewLine(writer);
try writer.print(
"Run '{s} <command> -h' or '{s} <command> --help' to get help for specific command",
Expand All @@ -65,8 +66,8 @@ fn writeHeader(self: *Help, writer: anytype) !void {
try writer.print("Usage: {s} ", .{self.cmd.name});

if (self.cmd.countArgs() >= 1) {
self.options.include_flags = true;
const braces = getBraces(self.cmd.setting.arg_required);
self.options.apply(.include_args);
const braces = getBraces(self.cmd.isSettingApplied(.arg_required));

for (self.cmd.args.items) |arg| {
try writer.print("{c}{s}{c} ", .{ braces[0], arg.name, braces[1] });
Expand All @@ -76,8 +77,8 @@ fn writeHeader(self: *Help, writer: anytype) !void {
if (self.cmd.countOptions() >= 1) try writer.writeAll("[OPTIONS] ");

if (self.cmd.countSubcommands() >= 1) {
self.options.include_subcmds = true;
const braces = getBraces(self.cmd.setting.subcommand_required);
self.options.apply(.include_subcmds);
const braces = getBraces(self.cmd.isSettingApplied(.subcommand_required));

try writer.print("{c}COMMAND{c}", .{ braces[0], braces[1] });
try writeNewLine(writer);
Expand All @@ -90,7 +91,7 @@ fn getBraces(required: bool) Braces {
}

fn writeCommands(self: *Help, writer: anytype) !void {
if (!(self.options.include_subcmds)) return;
if (!(self.options.isApplied(.include_subcmds))) return;

try writer.writeAll("Commands:");
try writeNewLine(writer);
Expand All @@ -104,7 +105,7 @@ fn writeCommands(self: *Help, writer: anytype) !void {
}

fn writeOptions(self: *Help, writer: anytype) !void {
if (self.options.include_flags) {
if (self.options.isApplied(.include_flags)) {
try writer.writeAll("Options:");
try writeNewLine(writer);

Expand All @@ -116,7 +117,7 @@ fn writeOptions(self: *Help, writer: anytype) !void {
if (arg.long_name) |long_name|
try writer.print(" --{s} ", .{long_name});

if (arg.settings.takes_value) {
if (arg.isSettingApplied(.takes_value)) {
// TODO: Add new `Arg.placeholderName()` to display proper placeholder

// Required options: <A | B | C>
Expand Down
11 changes: 5 additions & 6 deletions src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ test "arg required error" {
};

var app = try initAppArgs(allocator);
app.rootCommand().argRequired(true);
app.rootCommand().applySetting(.arg_required);
defer app.deinit();

try testing.expectError(error.CommandArgumentNotProvided, app.parseFrom(argv));
Expand All @@ -51,7 +51,7 @@ test "subcommand required error" {
};

var app = try initAppArgs(allocator);
app.rootCommand().subcommandRequired(true);
app.rootCommand().applySetting(.subcommand_required);
defer app.deinit();

try testing.expectError(error.CommandSubcommandNotProvided, app.parseFrom(argv));
Expand Down Expand Up @@ -112,11 +112,10 @@ test "arg.takes_multiple_values" {

var app = try initAppArgs(allocator);
defer app.deinit();
app.rootCommand().takesValue(true);
app.rootCommand().applySetting(.takes_value);

var files = Arg.new("files");
//files.takesValue(true);
files.takesMultipleValues(true);
files.applySetting(.takes_multiple_values);

try app.rootCommand().addArg(files);
var args = try app.parseFrom(argv);
Expand All @@ -131,7 +130,7 @@ test "using displayHelp and displaySubcommandHelp help api" {

var app = try initAppArgs(allocator);
defer app.deinit();
app.rootCommand().takesValue(false);
app.rootCommand().removeSetting(.takes_value);

var subcmd = app.createCommand("subcmd", null);
try subcmd.addArg(flag.boolean("bool", null, null));
Expand Down
Loading

0 comments on commit 77a3729

Please sign in to comment.