command, parser: try to clean up UserContext type handling
This is a feeble attempt to unify some logic, as I realized that Command.createInterface had different logic for handling the user context than Parser did, which broke certain use cases (using a slice as the context for example). I'm not convinced this really unifies the logic as much as wraps it in another layer of indirection, but at least the core problem is solved.
This commit is contained in:
parent
8bba68e5a9
commit
8ac610ae71
@ -72,6 +72,61 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
||||
};
|
||||
}
|
||||
|
||||
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 +137,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 +157,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 +177,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);
|
||||
@ -272,7 +330,7 @@ pub fn CommandBuilder(comptime UserContext: type) 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 {
|
||||
|
@ -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