Compare commits

..

No commits in common. "419d8994bac8601e2702554002234f546032175c" and "390a1ba4fda46b6ddaabe81d79497db7535bb454" have entirely different histories.

4 changed files with 38 additions and 119 deletions

View File

@ -113,16 +113,17 @@ pub fn main() !u8 {
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const base = try noclip.commandGroup(allocator, .{ .description = "base group" });
defer base.deinitTree();
var parser = try cli.createParser(cliHandler, allocator);
defer parser.deinitTree();
var context: u32 = 2;
const sc: []const u8 = "whassup";
try base.addSubcommand("main", try cli.createInterface(allocator, cliHandler, &context));
try base.addSubcommand("other", try subcommand.createInterface(allocator, subHandler, &sc));
var subcon = try subcommand.createParser(subHandler, allocator);
try parser.addSubcommand("verb", subcon.interface(&sc));
try base.execute();
const iface = parser.interface(&context);
iface.execute() catch return 1;
return 0;
}

View File

@ -72,76 +72,6 @@ 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 = .{},
@ -152,7 +82,6 @@ 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(),
@ -172,13 +101,11 @@ pub fn CommandBuilder(comptime UserContext: type) type {
};
}
pub usingnamespace InterfaceCreator(@This());
fn _createInterfaceImpl(
pub fn createInterface(
comptime self: @This(),
allocator: std.mem.Allocator,
comptime callback: self.CallbackSignature(),
context: (ICC.InputType() orelse void),
allocator: std.mem.Allocator,
context: UserContextType,
) !ParserInterface {
var arena = try allocator.create(std.heap.ArenaAllocator);
arena.* = std.heap.ArenaAllocator.init(allocator);
@ -192,7 +119,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
.help_builder = help.HelpBuilder(self).init(arena_alloc),
};
if (comptime ICC == .empty) {
if (UserContextType == void) {
return this_parser.interface();
} else {
return this_parser.interface(context);
@ -344,14 +271,8 @@ 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 (UserContextType, self.Output()) anyerror!void;
return *const fn (UserContext, self.Output()) anyerror!void;
}
pub fn Output(comptime self: @This()) type {

View File

@ -53,8 +53,6 @@ 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");
@ -229,6 +227,8 @@ 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,

View File

@ -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(), @TypeOf(command).ICC);
pub usingnamespace InterfaceGen(@This(), UserContext);
// 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,35 +536,32 @@ fn InterfaceWrappers(comptime ParserType: type) type {
};
}
// 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{}));
}
fn InterfaceGen(comptime ParserType: type, comptime UserContext: type) type {
const CtxInfo = @typeInfo(UserContext);
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));
}
return if (CtxInfo == .Void) struct {
pub fn interface(self: *ParserType) ParserInterface {
return ParserInterface.create(ParserType, self, @constCast(&void{}));
}
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, _: *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 @as(ICC.InputType().?, @ptrCast(@alignCast(ctx))).*;
}
},
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))).*;
}
};
}