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 {
|
pub fn CommandBuilder(comptime UserContext: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
param_spec: ncmeta.TupleBuilder = .{},
|
param_spec: ncmeta.TupleBuilder = .{},
|
||||||
@ -82,6 +137,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 +157,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 +177,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);
|
||||||
@ -272,7 +330,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -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,10 +536,10 @@ 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) {
|
||||||
return if (CtxInfo == .Void) struct {
|
.empty => struct {
|
||||||
pub fn interface(self: *ParserType) ParserInterface {
|
pub fn interface(self: *ParserType) ParserInterface {
|
||||||
return ParserInterface.create(ParserType, self, @constCast(&void{}));
|
return ParserInterface.create(ParserType, self, @constCast(&void{}));
|
||||||
}
|
}
|
||||||
@ -547,21 +547,24 @@ fn InterfaceGen(comptime ParserType: type, comptime UserContext: type) type {
|
|||||||
fn castContext(_: ParserType, _: *anyopaque) void {
|
fn castContext(_: ParserType, _: *anyopaque) void {
|
||||||
return void{};
|
return void{};
|
||||||
}
|
}
|
||||||
} else if (CtxInfo == .Pointer and CtxInfo.Pointer.size != .Slice) struct {
|
},
|
||||||
pub fn interface(self: *ParserType, context: UserContext) ParserInterface {
|
.pointer => struct {
|
||||||
|
pub fn interface(self: *ParserType, context: ICC.InputType().?) ParserInterface {
|
||||||
return ParserInterface.create(ParserType, self, @constCast(context));
|
return ParserInterface.create(ParserType, self, @constCast(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn castContext(_: ParserType, ctx: *anyopaque) UserContext {
|
fn castContext(_: ParserType, ctx: *anyopaque) ICC.OutputType() {
|
||||||
return @ptrCast(@alignCast(ctx));
|
return @ptrCast(@alignCast(ctx));
|
||||||
}
|
}
|
||||||
} else struct {
|
},
|
||||||
pub fn interface(self: *ParserType, context: *const UserContext) ParserInterface {
|
.value => struct {
|
||||||
|
pub fn interface(self: *ParserType, context: ICC.InputType().?) ParserInterface {
|
||||||
return ParserInterface.create(ParserType, self, @ptrCast(@constCast(context)));
|
return ParserInterface.create(ParserType, self, @ptrCast(@constCast(context)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn castContext(_: ParserType, ctx: *anyopaque) UserContext {
|
fn castContext(_: ParserType, ctx: *anyopaque) ICC.OutputType() {
|
||||||
return @as(*const UserContext, @ptrCast(@alignCast(ctx))).*;
|
return @as(ICC.InputType().?, @ptrCast(@alignCast(ctx))).*;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user