2022-11-20 12:54:26 -08:00
|
|
|
const std = @import("std");
|
|
|
|
const noclip = @import("noclip");
|
|
|
|
|
2023-03-30 17:00:49 -07:00
|
|
|
const CommandBuilder = noclip.CommandBuilder;
|
2022-11-20 12:54:26 -08:00
|
|
|
|
2023-03-30 17:00:49 -07:00
|
|
|
const Choice = enum { first, second };
|
2022-11-26 20:29:23 -08:00
|
|
|
|
2023-03-30 17:00:49 -07:00
|
|
|
const cli = cmd: {
|
parser: don't force pass userdata as a pointer
This is an interesting change. While I think generally passing in
constant userdata is not terribly useful, the previous implementation
precluded it entirely. Interface types, for example, are often passed
directly and stored as constants (they hold pointers to their mutable
state).
Since we type erase this so it can be bound to the generic interface
object, non-pointer objects must be passed by reference to avoid
binding the parser interface to a temporary stack copy of the object.
This means we have to handle these cases slightly differently. Also,
while technically being classified as pointers, slices don't really
behave like pointers, which is understandable but annoying. There's a
bit of asymmetry here, as CommandBuilder(*u32) and CommandBuilder
(u32) both require an *u32 when binding the parser interface. This is
of course because pointers do not need to be rewrapped to be type
erased. The same code path could be used for both cases, but then the
user would have to pass in a pointer to a pointer, which actually
looks a bit silly because then it potentially means having to
do &&my_var.
2023-04-08 15:13:00 -07:00
|
|
|
var cmd = CommandBuilder(*u32){
|
2023-04-04 23:22:12 -07:00
|
|
|
.description =
|
2023-04-01 13:04:02 -07:00
|
|
|
\\The definitive noclip demonstration utility
|
|
|
|
\\
|
2023-04-02 17:15:37 -07:00
|
|
|
\\This command demonstrates the functionality of the noclip library. cool!
|
2023-04-04 23:22:12 -07:00
|
|
|
,
|
|
|
|
};
|
2023-03-30 17:00:49 -07:00
|
|
|
cmd.add_option(.{ .OutputType = struct { u8, u8 } }, .{
|
|
|
|
.name = "test",
|
|
|
|
.short_tag = "-t",
|
|
|
|
.long_tag = "--test",
|
|
|
|
.env_var = "NOCLIP_TEST",
|
2023-04-02 15:11:50 -07:00
|
|
|
.description = "multi-value test option",
|
|
|
|
.nice_type_name = "int> <int",
|
2023-03-30 17:00:49 -07:00
|
|
|
});
|
|
|
|
cmd.add_option(.{ .OutputType = Choice }, .{
|
|
|
|
.name = "choice",
|
|
|
|
.short_tag = "-c",
|
|
|
|
.long_tag = "--choice",
|
2023-04-02 15:11:50 -07:00
|
|
|
.default = .second,
|
2023-03-30 17:00:49 -07:00
|
|
|
.env_var = "NOCLIP_CHOICE",
|
2023-04-02 15:11:50 -07:00
|
|
|
.description = "enum choice option",
|
|
|
|
.nice_type_name = "choice",
|
2023-03-30 17:00:49 -07:00
|
|
|
});
|
|
|
|
cmd.add_option(.{ .OutputType = u32 }, .{
|
|
|
|
.name = "default",
|
|
|
|
.short_tag = "-d",
|
|
|
|
.long_tag = "--default",
|
|
|
|
.env_var = "NOCLIP_DEFAULT",
|
|
|
|
.default = 100,
|
2023-04-02 15:11:50 -07:00
|
|
|
.description = "default value integer option",
|
|
|
|
.nice_type_name = "uint",
|
sorta help text generation
This mostly works. Subcommands are utterly broken because we blindly
consume an additional argument to get the program name, which we
should not do.
This code was always kind of spaghetti, but it's getting worse. I want
to refactor it into something that doesn't make me cringe, but at the
same time, this project was intended to be a means to an end rather
than the end itself, and it kind of feels a bit silly to spend a ton
of time on it. On the other hand, relying on it for other projects
seems silly if it's a fragile mess. The goal was to get it into a
usable state and then hack on it as necessary, but it still has a ways
to go to get there, and working on it is kind of painful, in an
existential fashion.
Perhaps I will attempt to rewrite it, get halfway, and stall forever.
Thanks for reading my cool commit message blog. Bye.
2023-03-20 23:13:58 -07:00
|
|
|
});
|
2023-03-30 17:00:49 -07:00
|
|
|
cmd.add_option(.{ .OutputType = u8, .multi = true }, .{
|
|
|
|
.name = "multi",
|
|
|
|
.short_tag = "-m",
|
|
|
|
.long_tag = "--multi",
|
2023-04-02 15:11:50 -07:00
|
|
|
.description = "multiple specification test option",
|
2022-11-27 01:31:20 -08:00
|
|
|
});
|
2023-03-30 17:00:49 -07:00
|
|
|
cmd.add_flag(.{}, .{
|
|
|
|
.name = "flag",
|
|
|
|
.truthy = .{ .short_tag = "-f", .long_tag = "--flag" },
|
2023-04-02 15:11:50 -07:00
|
|
|
.falsy = .{ .short_tag = "-F", .long_tag = "--no-flag" },
|
2023-03-30 17:00:49 -07:00
|
|
|
.env_var = "NOCLIP_FLAG",
|
2023-04-02 15:11:50 -07:00
|
|
|
.description = "boolean flag",
|
sorta help text generation
This mostly works. Subcommands are utterly broken because we blindly
consume an additional argument to get the program name, which we
should not do.
This code was always kind of spaghetti, but it's getting worse. I want
to refactor it into something that doesn't make me cringe, but at the
same time, this project was intended to be a means to an end rather
than the end itself, and it kind of feels a bit silly to spend a ton
of time on it. On the other hand, relying on it for other projects
seems silly if it's a fragile mess. The goal was to get it into a
usable state and then hack on it as necessary, but it still has a ways
to go to get there, and working on it is kind of painful, in an
existential fashion.
Perhaps I will attempt to rewrite it, get halfway, and stall forever.
Thanks for reading my cool commit message blog. Bye.
2023-03-20 23:13:58 -07:00
|
|
|
});
|
2023-03-30 17:00:49 -07:00
|
|
|
cmd.add_flag(.{ .multi = true }, .{
|
|
|
|
.name = "multiflag",
|
|
|
|
.truthy = .{ .short_tag = "-M" },
|
2023-04-02 15:11:50 -07:00
|
|
|
.description = "multiple specification test flag ",
|
2023-03-30 17:00:49 -07:00
|
|
|
});
|
2023-04-02 17:15:37 -07:00
|
|
|
cmd.add_option(.{ .OutputType = u8 }, .{
|
|
|
|
.name = "env",
|
|
|
|
.env_var = "NOCLIP_ENVIRON",
|
|
|
|
.description = "environment variable only option",
|
|
|
|
});
|
2023-03-30 17:00:49 -07:00
|
|
|
cmd.add_argument(.{ .OutputType = []const u8 }, .{
|
|
|
|
.name = "arg",
|
2023-04-02 17:15:37 -07:00
|
|
|
.description = "This is an argument that doesn't really do anything, but it's very important.",
|
sorta help text generation
This mostly works. Subcommands are utterly broken because we blindly
consume an additional argument to get the program name, which we
should not do.
This code was always kind of spaghetti, but it's getting worse. I want
to refactor it into something that doesn't make me cringe, but at the
same time, this project was intended to be a means to an end rather
than the end itself, and it kind of feels a bit silly to spend a ton
of time on it. On the other hand, relying on it for other projects
seems silly if it's a fragile mess. The goal was to get it into a
usable state and then hack on it as necessary, but it still has a ways
to go to get there, and working on it is kind of painful, in an
existential fashion.
Perhaps I will attempt to rewrite it, get halfway, and stall forever.
Thanks for reading my cool commit message blog. Bye.
2023-03-20 23:13:58 -07:00
|
|
|
});
|
2022-11-26 20:29:23 -08:00
|
|
|
|
2023-03-30 17:00:49 -07:00
|
|
|
break :cmd cmd;
|
2022-11-20 12:54:26 -08:00
|
|
|
};
|
|
|
|
|
2023-03-30 17:00:49 -07:00
|
|
|
const subcommand = cmd: {
|
parser: don't force pass userdata as a pointer
This is an interesting change. While I think generally passing in
constant userdata is not terribly useful, the previous implementation
precluded it entirely. Interface types, for example, are often passed
directly and stored as constants (they hold pointers to their mutable
state).
Since we type erase this so it can be bound to the generic interface
object, non-pointer objects must be passed by reference to avoid
binding the parser interface to a temporary stack copy of the object.
This means we have to handle these cases slightly differently. Also,
while technically being classified as pointers, slices don't really
behave like pointers, which is understandable but annoying. There's a
bit of asymmetry here, as CommandBuilder(*u32) and CommandBuilder
(u32) both require an *u32 when binding the parser interface. This is
of course because pointers do not need to be rewrapped to be type
erased. The same code path could be used for both cases, but then the
user would have to pass in a pointer to a pointer, which actually
looks a bit silly because then it potentially means having to
do &&my_var.
2023-04-08 15:13:00 -07:00
|
|
|
var cmd = CommandBuilder([]const u8){
|
2023-04-04 23:22:12 -07:00
|
|
|
.description =
|
2023-04-01 13:04:02 -07:00
|
|
|
\\Perform some sort of work
|
|
|
|
\\
|
|
|
|
\\This subcommand is a mystery. It probably does something, but nobody is sure what.
|
2023-04-04 23:22:12 -07:00
|
|
|
,
|
|
|
|
};
|
parser: don't force pass userdata as a pointer
This is an interesting change. While I think generally passing in
constant userdata is not terribly useful, the previous implementation
precluded it entirely. Interface types, for example, are often passed
directly and stored as constants (they hold pointers to their mutable
state).
Since we type erase this so it can be bound to the generic interface
object, non-pointer objects must be passed by reference to avoid
binding the parser interface to a temporary stack copy of the object.
This means we have to handle these cases slightly differently. Also,
while technically being classified as pointers, slices don't really
behave like pointers, which is understandable but annoying. There's a
bit of asymmetry here, as CommandBuilder(*u32) and CommandBuilder
(u32) both require an *u32 when binding the parser interface. This is
of course because pointers do not need to be rewrapped to be type
erased. The same code path could be used for both cases, but then the
user would have to pass in a pointer to a pointer, which actually
looks a bit silly because then it potentially means having to
do &&my_var.
2023-04-08 15:13:00 -07:00
|
|
|
cmd.simple_flag(.{
|
2023-03-30 17:00:49 -07:00
|
|
|
.name = "flag",
|
|
|
|
.truthy = .{ .short_tag = "-f", .long_tag = "--flag" },
|
|
|
|
.falsy = .{ .long_tag = "--no-flag" },
|
|
|
|
.env_var = "NOCLIP_SUBFLAG",
|
|
|
|
});
|
|
|
|
cmd.add_argument(.{ .OutputType = []const u8 }, .{ .name = "argument" });
|
|
|
|
break :cmd cmd;
|
|
|
|
};
|
2022-11-20 12:54:26 -08:00
|
|
|
|
parser: don't force pass userdata as a pointer
This is an interesting change. While I think generally passing in
constant userdata is not terribly useful, the previous implementation
precluded it entirely. Interface types, for example, are often passed
directly and stored as constants (they hold pointers to their mutable
state).
Since we type erase this so it can be bound to the generic interface
object, non-pointer objects must be passed by reference to avoid
binding the parser interface to a temporary stack copy of the object.
This means we have to handle these cases slightly differently. Also,
while technically being classified as pointers, slices don't really
behave like pointers, which is understandable but annoying. There's a
bit of asymmetry here, as CommandBuilder(*u32) and CommandBuilder
(u32) both require an *u32 when binding the parser interface. This is
of course because pointers do not need to be rewrapped to be type
erased. The same code path could be used for both cases, but then the
user would have to pass in a pointer to a pointer, which actually
looks a bit silly because then it potentially means having to
do &&my_var.
2023-04-08 15:13:00 -07:00
|
|
|
fn sub_handler(context: []const u8, result: subcommand.Output()) !void {
|
2023-03-30 17:00:49 -07:00
|
|
|
std.debug.print("subcommand: {s}\n", .{result.argument});
|
parser: don't force pass userdata as a pointer
This is an interesting change. While I think generally passing in
constant userdata is not terribly useful, the previous implementation
precluded it entirely. Interface types, for example, are often passed
directly and stored as constants (they hold pointers to their mutable
state).
Since we type erase this so it can be bound to the generic interface
object, non-pointer objects must be passed by reference to avoid
binding the parser interface to a temporary stack copy of the object.
This means we have to handle these cases slightly differently. Also,
while technically being classified as pointers, slices don't really
behave like pointers, which is understandable but annoying. There's a
bit of asymmetry here, as CommandBuilder(*u32) and CommandBuilder
(u32) both require an *u32 when binding the parser interface. This is
of course because pointers do not need to be rewrapped to be type
erased. The same code path could be used for both cases, but then the
user would have to pass in a pointer to a pointer, which actually
looks a bit silly because then it potentially means having to
do &&my_var.
2023-04-08 15:13:00 -07:00
|
|
|
std.debug.print("context: {s}\n", .{context});
|
2022-11-20 12:54:26 -08:00
|
|
|
}
|
|
|
|
|
2023-03-30 17:00:49 -07:00
|
|
|
fn cli_handler(context: *u32, result: cli.Output()) !void {
|
parser: don't force pass userdata as a pointer
This is an interesting change. While I think generally passing in
constant userdata is not terribly useful, the previous implementation
precluded it entirely. Interface types, for example, are often passed
directly and stored as constants (they hold pointers to their mutable
state).
Since we type erase this so it can be bound to the generic interface
object, non-pointer objects must be passed by reference to avoid
binding the parser interface to a temporary stack copy of the object.
This means we have to handle these cases slightly differently. Also,
while technically being classified as pointers, slices don't really
behave like pointers, which is understandable but annoying. There's a
bit of asymmetry here, as CommandBuilder(*u32) and CommandBuilder
(u32) both require an *u32 when binding the parser interface. This is
of course because pointers do not need to be rewrapped to be type
erased. The same code path could be used for both cases, but then the
user would have to pass in a pointer to a pointer, which actually
looks a bit silly because then it potentially means having to
do &&my_var.
2023-04-08 15:13:00 -07:00
|
|
|
std.debug.print("context: {d}\n", .{context.*});
|
2023-03-30 17:00:49 -07:00
|
|
|
std.debug.print("callback is working {any}\n", .{result.choice});
|
|
|
|
std.debug.print("callback is working {d}\n", .{result.default});
|
parser: don't force pass userdata as a pointer
This is an interesting change. While I think generally passing in
constant userdata is not terribly useful, the previous implementation
precluded it entirely. Interface types, for example, are often passed
directly and stored as constants (they hold pointers to their mutable
state).
Since we type erase this so it can be bound to the generic interface
object, non-pointer objects must be passed by reference to avoid
binding the parser interface to a temporary stack copy of the object.
This means we have to handle these cases slightly differently. Also,
while technically being classified as pointers, slices don't really
behave like pointers, which is understandable but annoying. There's a
bit of asymmetry here, as CommandBuilder(*u32) and CommandBuilder
(u32) both require an *u32 when binding the parser interface. This is
of course because pointers do not need to be rewrapped to be type
erased. The same code path could be used for both cases, but then the
user would have to pass in a pointer to a pointer, which actually
looks a bit silly because then it potentially means having to
do &&my_var.
2023-04-08 15:13:00 -07:00
|
|
|
context.* += 1;
|
2022-11-20 12:54:26 -08:00
|
|
|
}
|
|
|
|
|
2023-04-04 23:22:12 -07:00
|
|
|
pub fn main() !u8 {
|
2022-11-20 12:54:26 -08:00
|
|
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
|
|
|
defer arena.deinit();
|
|
|
|
const allocator = arena.allocator();
|
|
|
|
|
2023-04-01 13:04:02 -07:00
|
|
|
var parser = cli.create_parser(cli_handler, allocator);
|
2023-03-30 17:00:49 -07:00
|
|
|
var context: u32 = 2;
|
parser: don't force pass userdata as a pointer
This is an interesting change. While I think generally passing in
constant userdata is not terribly useful, the previous implementation
precluded it entirely. Interface types, for example, are often passed
directly and stored as constants (they hold pointers to their mutable
state).
Since we type erase this so it can be bound to the generic interface
object, non-pointer objects must be passed by reference to avoid
binding the parser interface to a temporary stack copy of the object.
This means we have to handle these cases slightly differently. Also,
while technically being classified as pointers, slices don't really
behave like pointers, which is understandable but annoying. There's a
bit of asymmetry here, as CommandBuilder(*u32) and CommandBuilder
(u32) both require an *u32 when binding the parser interface. This is
of course because pointers do not need to be rewrapped to be type
erased. The same code path could be used for both cases, but then the
user would have to pass in a pointer to a pointer, which actually
looks a bit silly because then it potentially means having to
do &&my_var.
2023-04-08 15:13:00 -07:00
|
|
|
const sc: []const u8 = "whassup";
|
2023-03-30 17:00:49 -07:00
|
|
|
|
2023-04-01 13:04:02 -07:00
|
|
|
var subcon = subcommand.create_parser(sub_handler, allocator);
|
parser: don't force pass userdata as a pointer
This is an interesting change. While I think generally passing in
constant userdata is not terribly useful, the previous implementation
precluded it entirely. Interface types, for example, are often passed
directly and stored as constants (they hold pointers to their mutable
state).
Since we type erase this so it can be bound to the generic interface
object, non-pointer objects must be passed by reference to avoid
binding the parser interface to a temporary stack copy of the object.
This means we have to handle these cases slightly differently. Also,
while technically being classified as pointers, slices don't really
behave like pointers, which is understandable but annoying. There's a
bit of asymmetry here, as CommandBuilder(*u32) and CommandBuilder
(u32) both require an *u32 when binding the parser interface. This is
of course because pointers do not need to be rewrapped to be type
erased. The same code path could be used for both cases, but then the
user would have to pass in a pointer to a pointer, which actually
looks a bit silly because then it potentially means having to
do &&my_var.
2023-04-08 15:13:00 -07:00
|
|
|
try parser.add_subcommand("verb", subcon.interface(&sc));
|
2023-03-30 17:00:49 -07:00
|
|
|
|
|
|
|
const iface = parser.interface(&context);
|
2023-04-04 23:22:12 -07:00
|
|
|
iface.execute() catch return 1;
|
|
|
|
|
|
|
|
return 0;
|
2022-11-20 12:54:26 -08:00
|
|
|
}
|