Compare commits
4 Commits
390a1ba4fd
...
419d8994ba
Author | SHA1 | Date | |
---|---|---|---|
419d8994ba | |||
a8652f71c4 | |||
8ac610ae71 | |||
8bba68e5a9 |
@ -113,17 +113,16 @@ pub fn main() !u8 {
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var parser = try cli.createParser(cliHandler, allocator);
|
||||
defer parser.deinitTree();
|
||||
const base = try noclip.commandGroup(allocator, .{ .description = "base group" });
|
||||
defer base.deinitTree();
|
||||
|
||||
var context: u32 = 2;
|
||||
const sc: []const u8 = "whassup";
|
||||
|
||||
var subcon = try subcommand.createParser(subHandler, allocator);
|
||||
try parser.addSubcommand("verb", subcon.interface(&sc));
|
||||
try base.addSubcommand("main", try cli.createInterface(allocator, cliHandler, &context));
|
||||
try base.addSubcommand("other", try subcommand.createInterface(allocator, subHandler, &sc));
|
||||
|
||||
const iface = parser.interface(&context);
|
||||
iface.execute() catch return 1;
|
||||
try base.execute();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -72,6 +72,76 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub const GroupOptions = struct {
|
||||
help_flag: ShortLongPair = .{ .short_tag = "-h", .long_tag = "--help" },
|
||||
description: []const u8,
|
||||
};
|
||||
|
||||
pub fn commandGroup(allocator: std.mem.Allocator, comptime options: GroupOptions) !ParserInterface {
|
||||
const cmd = comptime CommandBuilder(void){
|
||||
.help_flag = options.help_flag,
|
||||
.description = options.description,
|
||||
.subcommand_required = true,
|
||||
};
|
||||
|
||||
return try cmd.createInterface(allocator, cmd.noopCallback());
|
||||
}
|
||||
|
||||
fn InterfaceCreator(comptime Command: type) type {
|
||||
return if (Command.ICC.InputType()) |Type|
|
||||
struct {
|
||||
pub fn createInterface(
|
||||
comptime self: Command,
|
||||
allocator: std.mem.Allocator,
|
||||
comptime callback: self.CallbackSignature(),
|
||||
context: Type,
|
||||
) !ParserInterface {
|
||||
return try self._createInterfaceImpl(allocator, callback, context);
|
||||
}
|
||||
}
|
||||
else
|
||||
struct {
|
||||
pub fn createInterface(
|
||||
comptime self: Command,
|
||||
allocator: std.mem.Allocator,
|
||||
comptime callback: self.CallbackSignature(),
|
||||
) !ParserInterface {
|
||||
return try self._createInterfaceImpl(allocator, callback, void{});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub const InterfaceContextCategory = union(enum) {
|
||||
empty,
|
||||
pointer: type,
|
||||
value: type,
|
||||
|
||||
pub fn fromType(comptime ContextType: type) InterfaceContextCategory {
|
||||
return switch (@typeInfo(ContextType)) {
|
||||
.Void => .empty,
|
||||
.Pointer => |info| if (info.size == .Slice) .{ .value = ContextType } else .{ .pointer = ContextType },
|
||||
// technically, i0, u0, and struct{} should be treated as empty, probably
|
||||
else => .{ .value = ContextType },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn InputType(comptime self: InterfaceContextCategory) ?type {
|
||||
return switch (self) {
|
||||
.empty => null,
|
||||
.pointer => |Type| Type,
|
||||
.value => |Type| *const Type,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn OutputType(comptime self: InterfaceContextCategory) type {
|
||||
return switch (self) {
|
||||
.empty => void,
|
||||
.pointer => |Type| Type,
|
||||
.value => |Type| Type,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
return struct {
|
||||
param_spec: ncmeta.TupleBuilder = .{},
|
||||
@ -82,6 +152,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
description: []const u8,
|
||||
|
||||
pub const UserContextType = UserContext;
|
||||
pub const ICC: InterfaceContextCategory = InterfaceContextCategory.fromType(UserContextType);
|
||||
|
||||
pub fn createParser(
|
||||
comptime self: @This(),
|
||||
@ -101,11 +172,13 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn createInterface(
|
||||
pub usingnamespace InterfaceCreator(@This());
|
||||
|
||||
fn _createInterfaceImpl(
|
||||
comptime self: @This(),
|
||||
comptime callback: self.CallbackSignature(),
|
||||
allocator: std.mem.Allocator,
|
||||
context: UserContextType,
|
||||
comptime callback: self.CallbackSignature(),
|
||||
context: (ICC.InputType() orelse void),
|
||||
) !ParserInterface {
|
||||
var arena = try allocator.create(std.heap.ArenaAllocator);
|
||||
arena.* = std.heap.ArenaAllocator.init(allocator);
|
||||
@ -119,7 +192,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
.help_builder = help.HelpBuilder(self).init(arena_alloc),
|
||||
};
|
||||
|
||||
if (UserContextType == void) {
|
||||
if (comptime ICC == .empty) {
|
||||
return this_parser.interface();
|
||||
} else {
|
||||
return this_parser.interface(context);
|
||||
@ -271,8 +344,14 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
return self.param_spec.realTuple();
|
||||
}
|
||||
|
||||
pub fn noopCallback(comptime self: @This()) self.CallbackSignature() {
|
||||
return struct {
|
||||
fn callback(_: UserContextType, _: self.Output()) !void {}
|
||||
}.callback;
|
||||
}
|
||||
|
||||
pub fn CallbackSignature(comptime self: @This()) type {
|
||||
return *const fn (UserContext, self.Output()) anyerror!void;
|
||||
return *const fn (UserContextType, self.Output()) anyerror!void;
|
||||
}
|
||||
|
||||
pub fn Output(comptime self: @This()) type {
|
||||
|
@ -53,6 +53,8 @@ pub fn StructuredPrinter(comptime Writer: type) type {
|
||||
// TODO: lol return a real error
|
||||
if (indent >= self.wrap_width) return NoclipError.UnexpectedFailure;
|
||||
|
||||
if (text.len == 0) return;
|
||||
|
||||
// this assumes output stream has already had the first line properly
|
||||
// indented.
|
||||
var splitter = std.mem.split(u8, text, "\n");
|
||||
@ -227,8 +229,6 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
||||
|
||||
var just: usize = 0;
|
||||
inline for (comptime help_info.arguments) |arg| {
|
||||
if (comptime arg.description.len == 0) continue;
|
||||
|
||||
const pair: AlignablePair = .{
|
||||
.left = arg.name,
|
||||
.right = arg.description,
|
||||
|
@ -99,7 +99,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
||||
|
||||
// This is a slightly annoying hack to work around the fact that there's no way
|
||||
// to provide a method signature conditionally.
|
||||
pub usingnamespace InterfaceGen(@This(), UserContext);
|
||||
pub usingnamespace InterfaceGen(@This(), @TypeOf(command).ICC);
|
||||
// This is attached to the struct this way because these are all "private"
|
||||
// methods that exist exclusively to cast the type-erased interface object back
|
||||
// into something usable. Their implementations aren't meaningful and just
|
||||
@ -536,32 +536,35 @@ fn InterfaceWrappers(comptime ParserType: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
fn InterfaceGen(comptime ParserType: type, comptime UserContext: type) type {
|
||||
const CtxInfo = @typeInfo(UserContext);
|
||||
// TODO: figure out a better way of consolidating this logic with that in command.zig?
|
||||
fn InterfaceGen(comptime ParserType: type, comptime ICC: anytype) type {
|
||||
return switch (ICC) {
|
||||
.empty => struct {
|
||||
pub fn interface(self: *ParserType) ParserInterface {
|
||||
return ParserInterface.create(ParserType, self, @constCast(&void{}));
|
||||
}
|
||||
|
||||
return if (CtxInfo == .Void) struct {
|
||||
pub fn interface(self: *ParserType) ParserInterface {
|
||||
return ParserInterface.create(ParserType, self, @constCast(&void{}));
|
||||
}
|
||||
fn castContext(_: ParserType, _: *anyopaque) void {
|
||||
return void{};
|
||||
}
|
||||
},
|
||||
.pointer => struct {
|
||||
pub fn interface(self: *ParserType, context: ICC.InputType().?) ParserInterface {
|
||||
return ParserInterface.create(ParserType, self, @constCast(context));
|
||||
}
|
||||
|
||||
fn castContext(_: ParserType, _: *anyopaque) void {
|
||||
return void{};
|
||||
}
|
||||
} else if (CtxInfo == .Pointer and CtxInfo.Pointer.size != .Slice) struct {
|
||||
pub fn interface(self: *ParserType, context: UserContext) ParserInterface {
|
||||
return ParserInterface.create(ParserType, self, @constCast(context));
|
||||
}
|
||||
fn castContext(_: ParserType, ctx: *anyopaque) ICC.OutputType() {
|
||||
return @ptrCast(@alignCast(ctx));
|
||||
}
|
||||
},
|
||||
.value => struct {
|
||||
pub fn interface(self: *ParserType, context: ICC.InputType().?) ParserInterface {
|
||||
return ParserInterface.create(ParserType, self, @ptrCast(@constCast(context)));
|
||||
}
|
||||
|
||||
fn castContext(_: ParserType, ctx: *anyopaque) UserContext {
|
||||
return @ptrCast(@alignCast(ctx));
|
||||
}
|
||||
} else struct {
|
||||
pub fn interface(self: *ParserType, context: *const UserContext) ParserInterface {
|
||||
return ParserInterface.create(ParserType, self, @ptrCast(@constCast(context)));
|
||||
}
|
||||
|
||||
fn castContext(_: ParserType, ctx: *anyopaque) UserContext {
|
||||
return @as(*const UserContext, @ptrCast(@alignCast(ctx))).*;
|
||||
}
|
||||
fn castContext(_: ParserType, ctx: *anyopaque) ICC.OutputType() {
|
||||
return @as(ICC.InputType().?, @ptrCast(@alignCast(ctx))).*;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user