This is basically a full rewrite but with a much more solid concept of what the public API looks like, which has informed some of the lower-level decisions. This is not at feature parity with the main branch yet, but it does handle some things better. The main functionality missing is the help text generation and subcommands. There's still some design to think about on the subcommand side of things.
122 lines
4.0 KiB
Zig
122 lines
4.0 KiB
Zig
const std = @import("std");
|
|
|
|
const ParameterGenerics = @import("./doodle.zig").ParameterGenerics;
|
|
const CommandError = @import("./doodle.zig").Errors;
|
|
|
|
pub const ConversionError = error{
|
|
BadValue,
|
|
};
|
|
|
|
pub fn ConverterSignature(comptime gen: ParameterGenerics) type {
|
|
return if (gen.no_context())
|
|
*const fn ([]const u8) ConversionError!gen.ResultType()
|
|
else
|
|
*const fn (gen.ContextType, []const u8) ConversionError!gen.ResultType();
|
|
}
|
|
|
|
pub fn default_converter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
|
|
return switch (@typeInfo(gen.ResultType())) {
|
|
.Bool => flag_converter(gen),
|
|
.Int => int_converter(gen),
|
|
.Pointer => |info| if (info.size == .Slice and info.child == u8)
|
|
string_converter(gen)
|
|
else
|
|
null,
|
|
.Enum => choice_converter(gen),
|
|
else => null,
|
|
};
|
|
}
|
|
|
|
fn flag_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|
return if (gen.no_context())
|
|
struct {
|
|
pub fn handler(input: []const u8) ConversionError!bool {
|
|
// treat an empty string as falsy
|
|
if (input.len == 0) return false;
|
|
|
|
if (input.len <= 5) {
|
|
var lowerBuf: [5]u8 = undefined;
|
|
const comp = std.ascii.lowerString(&lowerBuf, input);
|
|
|
|
inline for ([_][]const u8{ "false", "no", "0" }) |candidate| {
|
|
if (std.mem.eql(u8, comp, candidate)) return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}.handler
|
|
else
|
|
struct {
|
|
pub fn handler(_: gen.ContextType, input: []const u8) ConversionError!bool {
|
|
// treat an empty string as falsy
|
|
if (input.len == 0) return false;
|
|
|
|
if (input.len <= 5) {
|
|
var lowerBuf: [5]u8 = undefined;
|
|
const comp = std.ascii.lowerString(&lowerBuf, input);
|
|
|
|
inline for ([_][]const u8{ "false", "no", "0" }) |candidate| {
|
|
if (std.mem.eql(u8, comp, candidate)) return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}.handler;
|
|
}
|
|
|
|
fn string_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|
return if (gen.no_context())
|
|
struct {
|
|
pub fn handler(value: []const u8) ConversionError![]const u8 {
|
|
return value;
|
|
}
|
|
}.handler
|
|
else
|
|
struct {
|
|
pub fn handler(_: gen.ContextType, value: []const u8) ConversionError![]const u8 {
|
|
return value;
|
|
}
|
|
}.handler;
|
|
}
|
|
|
|
fn int_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|
const IntType = gen.ResultType();
|
|
|
|
std.debug.assert(switch (@typeInfo(IntType)) {
|
|
.Int => true,
|
|
else => false,
|
|
});
|
|
|
|
return if (gen.no_context())
|
|
struct {
|
|
pub fn handler(value: []const u8) ConversionError!IntType {
|
|
return std.fmt.parseInt(IntType, value, 0) catch return ConversionError.BadValue;
|
|
}
|
|
}.handler
|
|
else
|
|
struct {
|
|
pub fn handler(_: gen.ContextType, value: []const u8) ConversionError!IntType {
|
|
return std.fmt.parseInt(IntType, value, 0) catch return ConversionError.BadValue;
|
|
}
|
|
}.handler;
|
|
}
|
|
|
|
fn choice_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|
const EnumType = gen.ResultType();
|
|
|
|
return if (gen.no_context())
|
|
struct {
|
|
pub fn handler(value: []const u8) ConversionError!EnumType {
|
|
return std.meta.stringToEnum(gen.ResultType(), value) orelse ConversionError.BadValue;
|
|
}
|
|
}.handler
|
|
else
|
|
struct {
|
|
pub fn handler(_: gen.ContextType, value: []const u8) ConversionError!EnumType {
|
|
return std.meta.stringToEnum(gen.ResultType(), value) orelse ConversionError.BadValue;
|
|
}
|
|
}.handler;
|
|
}
|