NOCLIP/demo/demo.zig
torque b1bac01257
all: start organizing into components and add user context support
The user can provide a context type and corresponding value that will
get passed into any executed callbacks. This allows for complex
behavior through side effects and provides a mechanism by which the
user can pass an allocator into argument handlers, etc.

There was also a lot of restructuring in this including a bit more
automagical behavior, like making parameters that wrap optional types
default to being optional. The start of automatic handler picking
(user overridable, of course) is in place as well.

Needing to specify the userdata context type makes things a bit more
verbose, and there's some other jank I'm interested in trying to
remove. I have some ideas, but I don't know how far I can go in my
abuse of the compiler.

However, this seems like it will be usable once I get around to writing
the help text generation.
2022-11-27 01:31:24 -08:00

88 lines
2.8 KiB
Zig

const std = @import("std");
const noclip = @import("noclip");
const context: []const u8 = "hello friend";
const ContextType = @TypeOf(context);
const helpFlag = noclip.HelpFlag(.{ .UserContext = ContextType });
const subData: noclip.CommandData = .{ .name = "subcommand", .help = "this a sub command" };
const subFlag: noclip.StringOption(ContextType) = .{ .name = "meta", .short = "-m" };
const subArg: noclip.StringArg(ContextType) = .{ .name = "sub" };
const subSpec = .{ helpFlag, subFlag, subArg };
const subCommand: noclip.CommandParser(subData, subSpec, ContextType, subCallback) = .{};
fn wrecker(zontext: ContextType, input: []const u8) ![]const u8 {
std.debug.print("ctx: {s}\n", .{zontext});
return input;
}
const cdata: noclip.CommandData = .{ .name = "main", .help = "main CLI entry point" };
const flagCheck: noclip.FlagOption(ContextType) = .{
.name = "flag",
.truthy = .{ .short = "-f", .long = "--flag" },
.falsy = .{ .long = "--no-flag" },
};
const inputOption: noclip.StringOption(ContextType) = .{
.name = "input",
.short = "-i",
.long = "--input",
.handler = wrecker,
.envVar = "OPTS_INPUT",
};
const outputOption: noclip.StringOption(ContextType) = .{ .name = "output", .long = "--output", .default = "waoh" };
const numberOption: noclip.ValuedOption(.{ .Output = i32, .UserContext = ContextType }) = .{ .name = "number", .short = "-n", .long = "--number" };
const argCheck: noclip.StringArg(ContextType) = .{ .name = "argument" };
const argAgain: noclip.StringArg(ContextType) = .{ .name = "another", .default = "nope" };
const mainSpec = .{
helpFlag,
flagCheck,
inputOption,
outputOption,
numberOption,
argCheck,
argAgain,
subCommand,
};
pub fn subCallback(_: ContextType, result: noclip.CommandResult(subSpec, ContextType)) !void {
std.debug.print("subcommand {any}!!!\n", .{result});
}
pub fn mainCommand(_: ContextType, result: noclip.CommandResult(mainSpec, ContextType)) !void {
std.debug.print(
\\arguments: {{
\\ .flag = {any}
\\ .input = {s}
\\ .output = {s}
\\ .number = {d}
\\ .argument = {s}
\\ .another = {s}
\\}}
\\
,
.{
result.flag,
result.input,
result.output,
result.number,
result.argument,
result.another,
},
);
}
pub fn main() !void {
var command: noclip.CommandParser(cdata, mainSpec, ContextType, mainCommand) = .{};
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = arena.allocator();
var argit = try std.process.argsWithAllocator(allocator);
_ = argit.next();
try command.execute(allocator, std.process.ArgIterator, &argit, context);
}