a whole big mess
This commit is contained in:
parent
ab1d4ad879
commit
11b7d3e06b
@ -43,17 +43,25 @@ const Main = struct {
|
|||||||
pub const string: noclip.Option(noclip.String) = .{
|
pub const string: noclip.Option(noclip.String) = .{
|
||||||
.short = 's',
|
.short = 's',
|
||||||
.long = "string",
|
.long = "string",
|
||||||
.env_var = "NOCLIP_STRING",
|
.env = "NOCLIP_STRING",
|
||||||
.description = "A string value option",
|
.description = "A string value option",
|
||||||
};
|
};
|
||||||
pub const default: noclip.Option(u32) = .{
|
pub const default: noclip.Option(u32) = .{
|
||||||
.name = "default",
|
.name = "default",
|
||||||
|
.description = "default value integer option",
|
||||||
.short = 'd',
|
.short = 'd',
|
||||||
.long = "default",
|
.long = "default",
|
||||||
.env_var = "NOCLIP_DEFAULT",
|
.env = "NOCLIP_DEFAULT",
|
||||||
.default = 100,
|
.default = 100,
|
||||||
|
// .nice_type_name = "uint",
|
||||||
|
};
|
||||||
|
pub const counter: noclip.Counter(u32) = .{
|
||||||
|
.name = "default",
|
||||||
.description = "default value integer option",
|
.description = "default value integer option",
|
||||||
.nice_type_name = "uint",
|
.short = 'd',
|
||||||
|
.long = "default",
|
||||||
|
.env = "NOCLIP_DEFAULT",
|
||||||
|
// .nice_type_name = "uint",
|
||||||
};
|
};
|
||||||
pub const multi: noclip.Option(noclip.Accumulate(u8)) = .{
|
pub const multi: noclip.Option(noclip.Accumulate(u8)) = .{
|
||||||
.name = "multi",
|
.name = "multi",
|
||||||
@ -61,7 +69,7 @@ const Main = struct {
|
|||||||
.long_tag = "multi",
|
.long_tag = "multi",
|
||||||
.description = "multiple specification test option",
|
.description = "multiple specification test option",
|
||||||
};
|
};
|
||||||
pub const flag: noclip.FlagSet = .{
|
pub const flag: noclip.BoolGroup = .{
|
||||||
.name = "flag",
|
.name = "flag",
|
||||||
.truthy = .{ .short = 'f', .long = "flag" },
|
.truthy = .{ .short = 'f', .long = "flag" },
|
||||||
.falsy = .{ .short = 'F', .long = "no-flag" },
|
.falsy = .{ .short = 'F', .long = "no-flag" },
|
||||||
@ -137,10 +145,10 @@ const Subcommand = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const parameters = struct {
|
pub const parameters = struct {
|
||||||
pub const flag: noclip.Flag = .{
|
pub const flag: noclip.BoolGroup = .{
|
||||||
.truthy = .{ .short = 'f', .long = "flag" },
|
.truthy = .{ .short = 'f', .long = "flag" },
|
||||||
.falsy = .{ .long = "no-flag" },
|
.falsy = .{ .long = "no-flag" },
|
||||||
.env_var = "NOCLIP_SUBFLAG",
|
.env = "NOCLIP_SUBFLAG",
|
||||||
};
|
};
|
||||||
pub const first_arg: noclip.Argument(noclip.String) = .{};
|
pub const first_arg: noclip.Argument(noclip.String) = .{};
|
||||||
pub const second_arg: noclip.Argument(noclip.String) = .{
|
pub const second_arg: noclip.Argument(noclip.String) = .{
|
||||||
|
@ -13,12 +13,26 @@ pub const CommandOptions = struct {
|
|||||||
|
|
||||||
const __Canary = opaque {};
|
const __Canary = opaque {};
|
||||||
|
|
||||||
pub const ErrorReport = struct {};
|
pub const ErrorReport = struct {
|
||||||
|
message: []const u8,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn Status(comptime T: type) type {
|
pub fn Status(comptime T: type) type {
|
||||||
return union(enum) {
|
return union(enum) {
|
||||||
success: T,
|
success: T,
|
||||||
failure: ErrorReport,
|
failure: ErrorReport,
|
||||||
|
|
||||||
|
pub fn succeed(arg: T) @This() {
|
||||||
|
return .{ .success = arg };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn failFull(arg: ErrorReport) @This() {
|
||||||
|
return .{ .failure = arg };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fail(msg: []const u8) @This() {
|
||||||
|
return .{ .failure = .{ .message = msg } };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +43,9 @@ pub const String = struct {
|
|||||||
pub const Codepoint = u21;
|
pub const Codepoint = u21;
|
||||||
|
|
||||||
pub const ParameterType = enum {
|
pub const ParameterType = enum {
|
||||||
flag,
|
bool_group,
|
||||||
|
constant,
|
||||||
|
counter,
|
||||||
// counter
|
// counter
|
||||||
// fixed_value
|
// fixed_value
|
||||||
// aggregate_flag
|
// aggregate_flag
|
||||||
@ -37,7 +53,7 @@ pub const ParameterType = enum {
|
|||||||
// aggregate_option
|
// aggregate_option
|
||||||
argument,
|
argument,
|
||||||
// aggregate_argument
|
// aggregate_argument
|
||||||
// group,
|
group,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Scope = enum { local, global };
|
pub const Scope = enum { local, global };
|
||||||
@ -68,9 +84,18 @@ pub fn Count(comptime T: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const FlagSet = struct {
|
pub const BoolGroup = struct {
|
||||||
pub const Result = bool;
|
pub const Result = bool;
|
||||||
pub const param_type: ParameterType = .flag;
|
pub const param_type: ParameterType = .bool_group;
|
||||||
|
pub const multi_mode: MultiMode = .last;
|
||||||
|
|
||||||
|
// accessors to easily read decls from an instance
|
||||||
|
pub fn Type(comptime _: *const BoolGroup) type {
|
||||||
|
return BoolGroup.Result;
|
||||||
|
}
|
||||||
|
pub fn mode(comptime _: *const BoolGroup) MultiMode {
|
||||||
|
return BoolGroup.multi_mode;
|
||||||
|
}
|
||||||
|
|
||||||
description: []const u8 = "",
|
description: []const u8 = "",
|
||||||
truthy: Pair = .{},
|
truthy: Pair = .{},
|
||||||
@ -102,25 +127,139 @@ pub const FlagSet = struct {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Counter = struct {
|
// figure this out: this is a zero-parameter flag that produces a non-boolean
|
||||||
pub const Result = u64;
|
// value, e.g. an int. for like -9 on gz. A flag is just a FixedValue with
|
||||||
pub const param_type: ParameterType = .flag;
|
pub fn Constant(comptime R: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
pub const Result = ScryResultType(R);
|
||||||
|
pub const param_type: ParameterType = .constant;
|
||||||
|
pub const multi_mode: MultiMode = scryMode(R);
|
||||||
|
|
||||||
description: []const u8 = "",
|
// accessors to easily read decls from an instance
|
||||||
short: ?Codepoint = null,
|
pub fn Type(comptime _: *const Self) type {
|
||||||
long: ?[]const u8 = null,
|
return Self.Result;
|
||||||
required: bool = false,
|
}
|
||||||
scope: Scope = .local,
|
pub fn mode(comptime _: *const Self) MultiMode {
|
||||||
hidden: bool = false,
|
return Self.multi_mode;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
description: []const u8 = "",
|
||||||
|
short: ?Codepoint = null,
|
||||||
|
long: ?[]const u8 = null,
|
||||||
|
env: ?[]const u8 = null,
|
||||||
|
/// Require that the user always provide a value for this option on the
|
||||||
|
/// command line.
|
||||||
|
required: bool = false,
|
||||||
|
/// The value associated with this flag
|
||||||
|
value: Result,
|
||||||
|
scope: Scope = .local,
|
||||||
|
eager: bool = false,
|
||||||
|
hidden: bool = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Counter(comptime R: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
pub const Result = ScryResultType(R);
|
||||||
|
pub const param_type: ParameterType = .counter;
|
||||||
|
pub const multi_mode: MultiMode = scryMode(R);
|
||||||
|
|
||||||
|
// accessors to easily read decls from an instance
|
||||||
|
pub fn Type(comptime _: *const Self) type {
|
||||||
|
return Self.Result;
|
||||||
|
}
|
||||||
|
pub fn mode(comptime _: *const Self) MultiMode {
|
||||||
|
return Self.multi_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
description: []const u8 = "",
|
||||||
|
short: ?Codepoint = null,
|
||||||
|
long: ?[]const u8 = null,
|
||||||
|
env: ?[]const u8 = null,
|
||||||
|
/// Require that the user always provide a value for this option on the
|
||||||
|
/// command line.
|
||||||
|
required: bool = false,
|
||||||
|
/// The value associated with this flag
|
||||||
|
increment: Result = 1,
|
||||||
|
scope: Scope = .local,
|
||||||
|
eager: bool = false,
|
||||||
|
hidden: bool = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Option(comptime R: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
pub const Result = ScryResultType(R);
|
||||||
|
pub const param_type: ParameterType = .option;
|
||||||
|
pub const multi_mode: MultiMode = scryMode(R);
|
||||||
|
|
||||||
|
// accessors to easily read decls from an instance
|
||||||
|
pub fn Type(comptime _: *const Self) type {
|
||||||
|
return Self.Result;
|
||||||
|
}
|
||||||
|
pub fn mode(comptime _: *const Self) MultiMode {
|
||||||
|
return Self.multi_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
description: []const u8 = "",
|
||||||
|
short: ?Codepoint = null,
|
||||||
|
long: ?[]const u8 = null,
|
||||||
|
env: ?[]const u8 = null,
|
||||||
|
/// Require that the user always provide a value for this option on the
|
||||||
|
/// command line.
|
||||||
|
required: bool = false,
|
||||||
|
/// A default value that will be forwarded if the option is not provided
|
||||||
|
/// on the command line by the user. If a default is provided, then the
|
||||||
|
/// corresponding parsed value will not be optional.
|
||||||
|
default: DefaultType(R) = defaultTypeDefault(R),
|
||||||
|
/// note: .count is only valid for flags
|
||||||
|
/// note: .accumulate requires R to be a slice
|
||||||
|
// multi: Multi = .last,
|
||||||
|
scope: Scope = .local,
|
||||||
|
eager: bool = false,
|
||||||
|
hidden: bool = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Argument(comptime R: type) type {
|
||||||
|
return struct {
|
||||||
|
const Self = @This();
|
||||||
|
pub const Result = ScryResultType(R);
|
||||||
|
pub const param_type: ParameterType = .argument;
|
||||||
|
pub const multi_mode: MultiMode = scryMode(R);
|
||||||
|
|
||||||
|
// accessors to easily read decls from an instance
|
||||||
|
pub fn Type(comptime _: *const Self) type {
|
||||||
|
return Self.Result;
|
||||||
|
}
|
||||||
|
pub fn mode(comptime _: *const Self) MultiMode {
|
||||||
|
return Self.multi_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
description: []const u8 = "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn Group(comptime R: type) type {
|
pub fn Group(comptime R: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
pub const Result = R;
|
const Self = @This();
|
||||||
// pub const param_type: ParameterType = .group;
|
pub const Result = ScryResultType(R);
|
||||||
|
pub const multi_mode: MultiMode = scryMode(R);
|
||||||
|
pub const param_type: ParameterType = .group;
|
||||||
|
|
||||||
// description: []const u8 = "",
|
// accessors to easily read decls from an instance
|
||||||
// env: ?[]const u8 = null,
|
pub fn Type(comptime _: *const Self) type {
|
||||||
|
return Self.Result;
|
||||||
|
}
|
||||||
|
pub fn mode(comptime _: *const Self) MultiMode {
|
||||||
|
return Self.multi_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
description: []const u8 = "",
|
||||||
|
env: ?[]const u8 = null,
|
||||||
/// at least one of the parameters in the group must be provided
|
/// at least one of the parameters in the group must be provided
|
||||||
required: bool = false,
|
required: bool = false,
|
||||||
parameters: type,
|
parameters: type,
|
||||||
@ -136,67 +275,42 @@ pub fn Group(comptime R: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// figure this out: this is a zero-parameter flag that produces a non-boolean
|
fn hasCanary(comptime T: type) bool {
|
||||||
// value, e.g. an int. for like -9 on gz. A flag is just a FixedValue with
|
return @hasDecl(T, "__noclip_canary__") and T.__noclip_canary__ == __Canary;
|
||||||
pub fn FixedValue(comptime R: type) type {
|
|
||||||
return struct {
|
|
||||||
pub const Result = R;
|
|
||||||
pub const param_type: ParameterType = .flag;
|
|
||||||
|
|
||||||
description: []const u8 = "",
|
|
||||||
short: ?Codepoint = null,
|
|
||||||
long: ?[]const u8 = null,
|
|
||||||
env: ?[]const u8 = null,
|
|
||||||
/// Require that the user always provide a value for this option on the
|
|
||||||
/// command line.
|
|
||||||
required: bool = false,
|
|
||||||
/// A default value that will be forwarded if the option is not provided
|
|
||||||
/// on the command line by the user. If a default is provided, then the
|
|
||||||
/// corresponding parsed value will not be optional.
|
|
||||||
value: Result,
|
|
||||||
scope: Scope = .local,
|
|
||||||
eager: bool = false,
|
|
||||||
hidden: bool = false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Option(comptime R: type) type {
|
pub fn scryMode(comptime T: type) MultiMode {
|
||||||
return struct {
|
return if (hasCanary(T))
|
||||||
pub const Result = R;
|
T.multi_mode
|
||||||
pub const param_type: ParameterType = .option;
|
else
|
||||||
|
.last;
|
||||||
description: []const u8 = "",
|
|
||||||
short: ?Codepoint = null,
|
|
||||||
long: ?[]const u8 = null,
|
|
||||||
env: ?[]const u8 = null,
|
|
||||||
/// Require that the user always provide a value for this option on the
|
|
||||||
/// command line.
|
|
||||||
required: bool = false,
|
|
||||||
/// A default value that will be forwarded if the option is not provided
|
|
||||||
/// on the command line by the user. If a default is provided, then the
|
|
||||||
/// corresponding parsed value will not be optional.
|
|
||||||
default: ?Result = null,
|
|
||||||
/// note: .count is only valid for flags
|
|
||||||
/// note: .accumulate requires R to be a slice
|
|
||||||
// multi: Multi = .last,
|
|
||||||
scope: Scope = .local,
|
|
||||||
eager: bool = false,
|
|
||||||
hidden: bool = false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Argument(comptime R: type) type {
|
pub fn ScryResultType(comptime T: type) type {
|
||||||
return struct {
|
return if (hasCanary(T)) switch (T.multi_mode) {
|
||||||
pub const Result = R;
|
.accumulate => []const T.Result,
|
||||||
|
.count, .first, .last => T.Result,
|
||||||
description: []const u8 = "",
|
} else T;
|
||||||
// note: equivalent to .accumulate. Requires R to be a slice
|
|
||||||
multi: bool = false,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute(comptime spec: type, args: ExecArgs(spec)) ReturnType(spec) {
|
pub fn DefaultType(comptime T: type) type {
|
||||||
var parser = Parser(spec).init(args.alloc, args.context);
|
return if (hasCanary(T)) switch (T.multi_mode) {
|
||||||
|
.accumulate => []const T.Result,
|
||||||
|
.count => T.Result,
|
||||||
|
.first, .last => ?T.Result,
|
||||||
|
} else ?T;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn defaultTypeDefault(comptime T: type) DefaultType(T) {
|
||||||
|
return if (hasCanary(T)) switch (T.multi_mode) {
|
||||||
|
.accumulate => &.{},
|
||||||
|
.count => 0,
|
||||||
|
.first, .last => null,
|
||||||
|
} else null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(comptime spec: type, args: ExecArgs(spec)) void {
|
||||||
|
var parser = Parser(spec, true).init(args.alloc, args.context);
|
||||||
defer parser.deinit();
|
defer parser.deinit();
|
||||||
switch (parser.parse(args.args, args.env)) {
|
switch (parser.parse(args.args, args.env)) {
|
||||||
.success => |callstack| {
|
.success => |callstack| {
|
||||||
|
@ -1,26 +1,10 @@
|
|||||||
fn Short(comptime spec: type) type {
|
pub fn Parser(comptime spec: type, comptime root: bool) type {
|
||||||
return struct {
|
return struct {
|
||||||
param: noclip.Codepoint,
|
|
||||||
eager: bool,
|
|
||||||
mutator: Mutator(spec),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn Long(comptime spec: type) type {
|
|
||||||
return struct {
|
|
||||||
param: []const u8,
|
|
||||||
eager: bool,
|
|
||||||
mutator: Mutator(spec),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn Parser(comptime spec: type) type {
|
|
||||||
return struct {
|
|
||||||
// this gets heap allocated because it cannot survive being copied
|
|
||||||
arena: *std.heap.ArenaAllocator,
|
arena: *std.heap.ArenaAllocator,
|
||||||
context: ContextType(spec),
|
context: ContextType(spec),
|
||||||
globals: GlobalParams,
|
globals: GlobalParams,
|
||||||
locals: LocalParams,
|
locals: LocalParams,
|
||||||
|
subcommands: Subcommands(spec, root),
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, context: ContextType(spec)) !Self {
|
pub fn init(alloc: std.mem.Allocator, context: ContextType(spec)) !Self {
|
||||||
const arena = try alloc.create(std.heap.ArenaAllocator);
|
const arena = try alloc.create(std.heap.ArenaAllocator);
|
||||||
@ -35,13 +19,15 @@ pub fn Parser(comptime spec: type) type {
|
|||||||
for (@typeInfo(@TypeOf(spec.parameters)).@"struct".decls) |dinf| {
|
for (@typeInfo(@TypeOf(spec.parameters)).@"struct".decls) |dinf| {
|
||||||
const decl = @field(@TypeOf(spec.parameters), dinf.name);
|
const decl = @field(@TypeOf(spec.parameters), dinf.name);
|
||||||
switch (@TypeOf(decl).param_type) {
|
switch (@TypeOf(decl).param_type) {
|
||||||
.flag => {
|
.bool_group => {
|
||||||
for (.{ "truthy", "falsy" }, .{ true, false }) |bias, value| {
|
for (.{ "truthy", "falsy" }, .{ true, false }) |bias, value| {
|
||||||
for (.{ "short", "long" }) |style| {
|
for (.{ "short", "long" }) |style| {
|
||||||
if (@field(@field(decl, bias), style)) |unw| {
|
if (@field(@field(decl, bias), style)) |unw| {
|
||||||
@field(@field(params, @tagName(decl.scope)), style) = @field(@field(params, @tagName(decl.scope)), style) ++ &.{
|
@field(@field(params, @tagName(decl.scope)), style) = @field(@field(params, @tagName(decl.scope)), style) ++ &.{
|
||||||
.{
|
.{
|
||||||
.param = unw,
|
.param = unw,
|
||||||
|
.eager = decl.eager,
|
||||||
|
.takes_value = false,
|
||||||
.mutator = implicitSetter(spec, dinf.name, value),
|
.mutator = implicitSetter(spec, dinf.name, value),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -49,17 +35,37 @@ pub fn Parser(comptime spec: type) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
.constant => {
|
||||||
|
for (.{ "short", "long" }) |style| {
|
||||||
|
if (@field(decl, style)) |unw| {
|
||||||
|
@field(@field(params, @tagName(decl.scope)), style) = @field(@field(params, @tagName(decl.scope)), style) ++ &.{.{
|
||||||
|
.param = unw,
|
||||||
|
.eager = decl.eager,
|
||||||
|
.takes_value = false,
|
||||||
|
.mutator = implicitSetter(spec, dinf.name, dinf.value),
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.counter => {},
|
||||||
.option => {
|
.option => {
|
||||||
for (.{ "short", "long" }) |style| {
|
for (.{ "short", "long" }) |style| {
|
||||||
if (@field(decl, style)) |unw| {
|
if (@field(decl, style)) |unw| {
|
||||||
@field(@field(params, @tagName(decl.scope)), style) = @field(@field(params, @tagName(decl.scope)), style) ++ &.{.{
|
@field(@field(params, @tagName(decl.scope)), style) = @field(@field(params, @tagName(decl.scope)), style) ++ &.{.{
|
||||||
.param = unw,
|
.param = unw,
|
||||||
|
.eager = decl.eager,
|
||||||
|
.takes_value = true,
|
||||||
.mutator = defaultMutator(spec, dinf.name),
|
.mutator = defaultMutator(spec, dinf.name),
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.argument => {},
|
.argument => {
|
||||||
|
params.local.args = params.local.args ++ &.{
|
||||||
|
defaultMutator(spec, dinf.name),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.group => {},
|
||||||
}
|
}
|
||||||
break :blk .{ params.global, params.local };
|
break :blk .{ params.global, params.local };
|
||||||
}
|
}
|
||||||
@ -79,14 +85,111 @@ pub fn Parser(comptime spec: type) type {
|
|||||||
pa.destroy(self.arena);
|
pa.destroy(self.arena);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse(self: Self, args: []const [:0]const u8, env: std.process.EnvMap) noclip.Status(void) {
|
||||||
|
const alloc = self.arena.allocator();
|
||||||
|
var argt = ArgTraveler.fromSlice(alloc, args) catch return .fail("out of memory");
|
||||||
|
// pre-parse globals. globals can only be named, which simplifies things
|
||||||
|
var result = defaultInit(Result(spec));
|
||||||
|
while (argt.current) |node| : (argt.next()) {
|
||||||
|
const arg = node.data;
|
||||||
|
if (arg.len > 2 and arg[0] == '-' and arg[1] == '-') {
|
||||||
|
if (self.globals.long.get(arg[2..])) |pctx| {
|
||||||
|
argt.drop();
|
||||||
|
const value: [:0]const u8 = if (pctx.takes_value)
|
||||||
|
argt.popNext()
|
||||||
|
else
|
||||||
|
"";
|
||||||
|
switch (pctx.mutator(alloc, self.context, &result, value)) {}
|
||||||
|
}
|
||||||
|
} else if (arg.len > 1 and arg[0] == '-') {
|
||||||
|
const view = std.unicode.Utf8View.init(arg[1..]) catch return .fail("thats not valid utf8");
|
||||||
|
var iter = view.iterator();
|
||||||
|
while (iter.nextCodepointSlice()) |seq| {
|
||||||
|
if (self.globals.short.get(seq)) {
|
||||||
|
// we have to drop this byte sequence within the fused short params. ugly..................... hrngrk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// var parse_mode: enum { mixed, ordered } = .mixed;
|
||||||
|
|
||||||
|
// for (args) |arg| {
|
||||||
|
// if (arg.len > 2 and arg[0] == '-' and arg[1] == '-') {}
|
||||||
|
// // if (arg.len > 0 and arg[0] == '-')
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
const NamedParameter = struct {
|
||||||
|
eager: bool,
|
||||||
|
takes_value: bool,
|
||||||
|
mutator: Mutator(spec),
|
||||||
|
};
|
||||||
|
|
||||||
|
const PMap = std.StaticStringMap(NamedParameter);
|
||||||
|
const ArgList = std.SinglyLinkedList([:0]const u8);
|
||||||
|
|
||||||
|
pub const ArgTraveler = struct {
|
||||||
|
first: ?*Node = null,
|
||||||
|
current: ?*Node = null,
|
||||||
|
prev: ?*Node = null,
|
||||||
|
mem: []const Node,
|
||||||
|
|
||||||
|
pub fn fromSlice(alloc: std.mem.Allocator, slice: []const [:0]const u8) error{OutOfMemory}!ArgTraveler {
|
||||||
|
if (slice.len == 0) return .{ .mem = &.{} };
|
||||||
|
|
||||||
|
const nmem = try alloc.alloc(Node, slice.len);
|
||||||
|
nmem[0] = slice[0];
|
||||||
|
for (slice[1..], nmem[1..], nmem[0 .. nmem.len - 1]) |arg, *current, *prev| {
|
||||||
|
current.* = .{ .data = arg };
|
||||||
|
prev.next = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
return .{ .first = &nmem[0], .current = &nmem[0], .mem = nmem };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(self: *ArgTraveler) void {
|
||||||
|
self.current = self.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(self: *ArgTraveler) void {
|
||||||
|
self.prev = self.current;
|
||||||
|
if (self.current) |current| {
|
||||||
|
self.current = current.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drop(self: *ArgTraveler) void {
|
||||||
|
if (self.current == null) return;
|
||||||
|
|
||||||
|
if (self.current == self.first)
|
||||||
|
self.first = self.current.?.next
|
||||||
|
else if (self.prev) |prev|
|
||||||
|
prev.next = self.current.?.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn popNext(self: *ArgTraveler) ?*Node {
|
||||||
|
self.next();
|
||||||
|
defer self.drop();
|
||||||
|
return self.current;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Node = struct {
|
||||||
|
data: [:0]const u8,
|
||||||
|
next: ?*Node = null,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// const PMap = std.StringHashMap(NamedParameter);
|
||||||
|
|
||||||
const GlobalParams = struct {
|
const GlobalParams = struct {
|
||||||
short: []const Short(spec),
|
short: PMap,
|
||||||
long: []const Long(spec),
|
long: PMap,
|
||||||
};
|
};
|
||||||
const LocalParams = struct {
|
const LocalParams = struct {
|
||||||
short: []const Short(spec),
|
short: PMap,
|
||||||
long: []const Long(spec),
|
long: PMap,
|
||||||
args: []const Mutator(spec),
|
args: []const Mutator(spec),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -104,14 +207,16 @@ pub fn Result(comptime spec: type) type {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (@typeInfo(@TypeOf(spec.parameters)).@"struct".decls) |df| {
|
for (@typeInfo(@TypeOf(spec.parameters)).@"struct".decls) |df| {
|
||||||
const decl = @field(spec.parameters, df.name);
|
const param = @field(spec.parameters, df.name);
|
||||||
const ftype = if (decl.default != null) @TypeOf(decl).Result else ?@TypeOf(decl).Result;
|
if (@TypeOf(param).Result == void) continue;
|
||||||
|
|
||||||
|
const FType = ResultFieldType(param);
|
||||||
out.@"struct".fields = out.@"struct".fields ++ &.{.{
|
out.@"struct".fields = out.@"struct".fields ++ &.{.{
|
||||||
.name = df.name,
|
.name = df.name ++ "",
|
||||||
.type = ftype,
|
.type = FType,
|
||||||
.default_value = decl.default orelse null,
|
.default_value = resultFieldDefault(param),
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = @alignOf(ftype),
|
.alignment = @alignOf(FType),
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +224,127 @@ pub fn Result(comptime spec: type) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn defaultInit(comptime T: type) T {
|
||||||
|
var result: T = undefined;
|
||||||
|
|
||||||
|
for (@typeInfo(T).Struct.fields) |field| {
|
||||||
|
if (field.default_value) |def| {
|
||||||
|
@field(result, field.name) = @as(*const field.type, @ptrCast(@alignCast(def))).*;
|
||||||
|
} else switch (@typeInfo(field.type)) {
|
||||||
|
.@"struct" => @field(result, field.name) = defaultInit(field.type),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ResultFieldType(comptime param: anytype) type {
|
||||||
|
if (param.mode() == .accumulate) {
|
||||||
|
return param.Type();
|
||||||
|
}
|
||||||
|
if (@typeInfo(param.Type()) == .optional) {
|
||||||
|
return if (param.default != null or param.required)
|
||||||
|
param.Type()
|
||||||
|
else
|
||||||
|
?param.Type();
|
||||||
|
} else @compileError("you stepped in it now");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resultFieldDefault(comptime param: anytype) ?*anyopaque {
|
||||||
|
if (param.mode() == .accumulate) {
|
||||||
|
return ¶m.default;
|
||||||
|
}
|
||||||
|
if (@typeInfo(param.Type()) == .optional) {
|
||||||
|
return if (param.default) |def|
|
||||||
|
&@as(param.Type(), def)
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
} else @compileError("doom");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Subcommands(comptime spec: type, comptime root: bool) type {
|
||||||
|
comptime {
|
||||||
|
if (!@hasDecl(spec, "subcommands")) return void;
|
||||||
|
const decls = @typeInfo(@TypeOf(spec.subcommands)).@"struct".decls;
|
||||||
|
if (decls.len == 0) return void;
|
||||||
|
|
||||||
|
var out: std.builtin.Type = .{
|
||||||
|
.@"struct" = .{
|
||||||
|
.layout = .auto,
|
||||||
|
.fields = &.{},
|
||||||
|
.decls = &.{},
|
||||||
|
.is_tuple = false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (decls) |dinf| {
|
||||||
|
const decl = @field(@TypeOf(spec.subcommands), dinf.name);
|
||||||
|
const FType = Parser(decl, false);
|
||||||
|
out.@"struct".fields = out.@"struct".fields ++ &.{.{
|
||||||
|
.name = dinf.name + "",
|
||||||
|
.type = FType,
|
||||||
|
.default_value = null,
|
||||||
|
.is_comptime = false,
|
||||||
|
.alignment = @alignOf(FType),
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
if (root) {
|
||||||
|
// help: switch (spec.options.create_help_command) {
|
||||||
|
switch (spec.options.create_help_command) {
|
||||||
|
// .if_subcommands => if (out.@"struct".fields.len > 0) continue :help .always,
|
||||||
|
.if_subcommands,
|
||||||
|
.always,
|
||||||
|
=> {
|
||||||
|
const FType = Parser(HelpCommand(spec), false);
|
||||||
|
out.@"struct".fields = out.@"struct".fields ++ &.{.{
|
||||||
|
.name = "help",
|
||||||
|
.type = FType,
|
||||||
|
.default_value = null,
|
||||||
|
.is_comptime = false,
|
||||||
|
.alignment = @alignOf(FType),
|
||||||
|
}};
|
||||||
|
},
|
||||||
|
.never => {},
|
||||||
|
}
|
||||||
|
if (spec.options.create_completion_helper) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return @Type(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn HelpCommand(comptime rootspec: type) type {
|
||||||
|
return struct {
|
||||||
|
pub const description =
|
||||||
|
\\Get detailed help for a subcommand
|
||||||
|
;
|
||||||
|
pub const options: noclip.CommandOptions = .{};
|
||||||
|
pub const parameters = struct {
|
||||||
|
command_path: noclip.Argument(noclip.Aggregate(noclip.String)) = .{
|
||||||
|
.description =
|
||||||
|
\\The name of the subcommand to print help for. Nested subcommands
|
||||||
|
\\can be requested as well.
|
||||||
|
,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn run(args: Result(@This())) void {
|
||||||
|
HelpGenerator(rootspec).lookupHelp(args.command_path);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn HelpGenerator(comptime rootspec: type) type {
|
||||||
|
return struct {
|
||||||
|
pub fn lookupHelp(command_path: []const noclip.String) void {
|
||||||
|
_ = rootspec;
|
||||||
|
_ = command_path;
|
||||||
|
std.debug.print("This is a stub\n", .{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn FieldType(comptime T: type, comptime field: []const u8) type {
|
pub fn FieldType(comptime T: type, comptime field: []const u8) type {
|
||||||
// return @FieldType(T, field);
|
// return @FieldType(T, field);
|
||||||
return switch (@typeInfo(T)) {
|
return switch (@typeInfo(T)) {
|
||||||
@ -250,15 +476,15 @@ pub fn convertInt(comptime T: type, base: u8) SimpleConverter(T, true) {
|
|||||||
return struct {
|
return struct {
|
||||||
fn conv(alloc: std.mem.Allocator, input: []const u8) noclip.Status(T) {
|
fn conv(alloc: std.mem.Allocator, input: []const u8) noclip.Status(T) {
|
||||||
return if (std.fmt.parseInt(FieldType, input, base)) |res|
|
return if (std.fmt.parseInt(FieldType, input, base)) |res|
|
||||||
.{ .success = res }
|
.succeed(res)
|
||||||
else |_|
|
else |_|
|
||||||
.{ .failure = .{
|
.fail(
|
||||||
.message = std.fmt.allocPrint(
|
std.fmt.allocPrint(
|
||||||
alloc,
|
alloc,
|
||||||
"could not parse {s} as an integer",
|
"could not parse {s} as an integer",
|
||||||
.{input},
|
.{input},
|
||||||
) catch "out of memory",
|
) catch "out of memory",
|
||||||
} };
|
);
|
||||||
}
|
}
|
||||||
}.conv;
|
}.conv;
|
||||||
}
|
}
|
||||||
@ -267,24 +493,24 @@ pub fn convertEnum(comptime T: type) SimpleConverter(T, true) {
|
|||||||
return struct {
|
return struct {
|
||||||
fn conv(alloc: std.mem.Allocator, input: []const u8) noclip.Status(T) {
|
fn conv(alloc: std.mem.Allocator, input: []const u8) noclip.Status(T) {
|
||||||
return if (std.meta.stringToEnum(T, input)) |val|
|
return if (std.meta.stringToEnum(T, input)) |val|
|
||||||
.{ .success = val }
|
.succeed(val)
|
||||||
else
|
else
|
||||||
.{
|
.fail(
|
||||||
.failure = .{ .message = std.fmt.allocPrint(
|
std.fmt.allocPrint(
|
||||||
alloc,
|
alloc,
|
||||||
"`{s}` is not a member of {s}",
|
"`{s}` is not a member of {s}",
|
||||||
.{ input, @typeName(T) } catch "out of memory",
|
.{ input, @typeName(T) },
|
||||||
) },
|
) catch "out of memory",
|
||||||
};
|
);
|
||||||
}
|
}
|
||||||
}.conv;
|
}.conv;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convertString(alloc: std.mem.Allocator, input: []const u8) noclip.Status(noclip.String) {
|
pub fn convertString(alloc: std.mem.Allocator, input: []const u8) noclip.Status(noclip.String) {
|
||||||
return if (alloc.dupe(input)) |copy|
|
return if (alloc.dupe(input)) |copy|
|
||||||
.{ .success = .{ .bytes = copy } }
|
.succeed(.{ .bytes = copy })
|
||||||
else |_|
|
else |_|
|
||||||
.{ .failure = .{ .message = "out of memory" } };
|
.fail("out of memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn incrementor(
|
fn incrementor(
|
||||||
@ -294,7 +520,7 @@ fn incrementor(
|
|||||||
) Mutator(spec) {
|
) Mutator(spec) {
|
||||||
return struct {
|
return struct {
|
||||||
fn mut(_: std.mem.Allocator, _: ContextType(spec), res: *Result(spec), _: []const u8) noclip.Status(void) {
|
fn mut(_: std.mem.Allocator, _: ContextType(spec), res: *Result(spec), _: []const u8) noclip.Status(void) {
|
||||||
@field(res, field) += step;
|
@field(res, field) +|= step;
|
||||||
return .success;
|
return .success;
|
||||||
}
|
}
|
||||||
}.mut;
|
}.mut;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user