I have just pushed a new version (0.6.1) of cligen, the (Python) program that generated the (Zig) source for the commandline completion in zigclc to PyPI. With this version you can generate the Zig code for zigclc or for some other Zig program yourself.
cligen now also allows for accessing the result of the commandline parsing in your program (something that was not necessary
for zigclc and not available in a not-released earlier version). cligen generates pure zig source code for command-line parsing of (nested) sub commands,
options (long, short, single-dash-long options) and arguments, which have types and (optional) restrictions (like
a set of arguments to option offered for completion and checked against the actual commandline).
Restrictions, types, actions and dynamic help/completion generation is user-extensible.
An example specification:
!Cli 0:
- !Source cli.zig
- !Instance
- !Complete
- !Opt [verbose, !Action count, !Help Increase program verbosity]
- init:
- !Option
- type
- T
- !Restriction [basic: minimal example code, complex: full-fledged example code]
- !Help type of utility
- !Arg [name]
- gen:
- zig: # sub-sub cmd
- !Option [version, !Help Zig version to use]
- !Option [fast, !Type bool]
This defines two subcommands, with the second having a sub-subcommand. The generated code will be written in source file cli.zig.
In your main.zig file you have to import the source file and call its command_line_parser() function:
const std = @import("std");
const cli = @import("cli.zig");
pub fn main(init: std.process.Init) !void {
const args = try cli.command_line_parser(init);
switch (args) {
._base => |cmd| std.debug.print("verbose {?}\n", .{cmd.verbose}),
.init => |cmd| std.debug.print("verbose: {?}, type: '{?s}'\n", .{cmd.verbose, cmd.type}),
.gen => |cmd| std.debug.print("verbose: {?}, fast: {?}\n", .{cmd.verbose, cmd.fast}),
.gen_zig => |cmd| std.debug.print("verbose: {?}, fast: {?}, version: '{?s}'\n", .{cmd.verbose, cmd.fast, cmd.version}),
}
}
The line - !Instance in the spec indicates that you process the values set for the options/arguments, by switching
on the result of command_line_parser().
Alternatively you can replace - !Instance with - !StructFunction '{}_sub_command' and process by calling struct_function on the result and passing a structure to command_line_parser():
const std = @import("std");
const cli = @import("cli.zig");
const Dispatcher = struct {
out: *std.Io.Writer,
pub fn init(writer: *std.Io.Writer) Dispatcher {
return .{
.out = writer,
};
}
pub fn _base_sub_command(self: *Dispatcher, verbose: ?i32) !void {
try self.out.print("verbose: {?}\n", .{verbose});
}
pub fn init_sub_command(self: *Dispatcher, verbose: ?i32, typ: ?[]const u8, name: ?[]const u8) !void {
try self.out.print("verbose: {?}, type: '{?s}', name: '{?s}'\n", .{verbose, typ, name});
}
pub fn gen_sub_command(self: *Dispatcher, verbose: ?i32, fast: ?bool) !void {
try self.out.print("verbose: {?}, fast: {?}\n", .{verbose, fast});
}
pub fn gen_zig_sub_command(self: *Dispatcher, verbose: ?i32, fast: ?bool, version: ?[]const u8) !void {
try self.out.print("verbose: {?}, fast: {?}, version: {?s}\n", .{verbose, fast, version});
}
};
pub fn main(init: std.process.Init) !void {
var std_out_buf: [1024]u8 = undefined;
var std_out_writer = std.Io.File.stdout().writer(init.io, &std_out_buf);
const args = try cli.command_line_parser(init);
var disp = Dispatcher.init(&std_out_writer.interface);
try args.struct_function(&disp);
_ = &std_out_writer.interface.flush();
}
cligen analyse can be used to analyse mostly-two-column help output (such as zig subcommand --help prints), to generate a YAML specification.
- to install
cligenyou need to have a relatively recent python on your system and runpython -m pip install cligen - if you are intersted in having
cligengenerate your commandline parsing code, including help and<TAB>-completion (forzsh,fishandbash), but don’t have the
time to try this out yourself (especially with the limited documentation currently available), let me know and I generate an example YAML and Zig output, based on your current help output and/or subcommand/option/argument description. - if you have a (good) case for a different way of accessing the command-line parsing results, than the two examples above, let me know and I will consider adding that to
cligen. cligencan generate0.15compatible code (in which case you don’t passinittocommand_line_parser)cligenshould probably be called frombuild.zig, I really have to spent some time with the Zig build system and make examples of how to incorporate that.
And in case you are wondering: yes I am considering rewriting cligen in Zig, but I currently have other priorities. And please realise that cligen only needs to be run if you change your commandline options/arguments/sub-commands.