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