2023-03-25 16:08:59 -07:00
|
|
|
const std = @import("std");
|
|
|
|
|
|
|
|
const ParameterGenerics = @import("./doodle.zig").ParameterGenerics;
|
|
|
|
const CommandError = @import("./doodle.zig").Errors;
|
2023-03-29 23:10:42 -07:00
|
|
|
const ValueCount = @import("./doodle.zig").ValueCount;
|
|
|
|
const ParseError = @import("./doodle.zig").ParseError;
|
|
|
|
const ncmeta = @import("./meta.zig");
|
2023-03-25 16:08:59 -07:00
|
|
|
|
|
|
|
pub fn ConverterSignature(comptime gen: ParameterGenerics) type {
|
2023-03-29 23:10:42 -07:00
|
|
|
return *const fn (*gen.UserContext, gen.IntermediateType()) ParseError!gen.ConvertedType();
|
2023-03-25 16:08:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn default_converter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
|
2023-03-29 23:10:42 -07:00
|
|
|
return if (comptime gen.multi)
|
|
|
|
multi_converter(gen)
|
|
|
|
else switch (@typeInfo(gen.OutputType)) {
|
2023-03-25 16:08:59 -07:00
|
|
|
.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),
|
2023-03-29 23:10:42 -07:00
|
|
|
// TODO: how to handle structs with field defaults? maybe this should only work
|
|
|
|
// for tuples, which I don't think can have defaults.
|
|
|
|
.Struct => |info| if (gen.value_count == .fixed and gen.value_count.fixed == info.fields.len)
|
|
|
|
struct_converter(gen)
|
|
|
|
else
|
|
|
|
null,
|
2023-03-25 16:08:59 -07:00
|
|
|
else => null,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-03-29 23:10:42 -07:00
|
|
|
fn multi_converter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
|
|
|
|
const converter = default_converter(
|
|
|
|
ncmeta.copy_struct(ParameterGenerics, gen, .{ .multi = false }),
|
|
|
|
) orelse
|
|
|
|
@compileError("no default converter");
|
|
|
|
const Intermediate = gen.IntermediateType();
|
2023-03-28 23:35:54 -07:00
|
|
|
|
2023-03-29 23:10:42 -07:00
|
|
|
return struct {
|
|
|
|
pub fn handler(context: *gen.UserContext, input: Intermediate) ParseError!std.ArrayList(gen.OutputType) {
|
|
|
|
var output = std.ArrayList(gen.OutputType).initCapacity(input.allocator, input.items.len) catch
|
|
|
|
return ParseError.ConversionFailed;
|
2023-03-28 23:35:54 -07:00
|
|
|
|
2023-03-29 23:10:42 -07:00
|
|
|
for (input.items) |item| {
|
|
|
|
output.appendAssumeCapacity(try converter(context, item));
|
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
}.handler;
|
|
|
|
}
|
2023-03-28 23:35:54 -07:00
|
|
|
|
2023-03-25 16:08:59 -07:00
|
|
|
fn flag_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
2023-03-28 01:07:02 -07:00
|
|
|
return struct {
|
2023-03-29 23:10:42 -07:00
|
|
|
pub fn handler(_: *gen.UserContext, input: []const u8) ParseError!bool {
|
2023-03-28 01:07:02 -07:00
|
|
|
// treat an empty string as falsy
|
|
|
|
if (input.len == 0) return false;
|
2023-03-25 16:08:59 -07:00
|
|
|
|
2023-03-28 01:07:02 -07:00
|
|
|
if (input.len <= 5) {
|
|
|
|
var lowerBuf: [5]u8 = undefined;
|
|
|
|
const comp = std.ascii.lowerString(&lowerBuf, input);
|
2023-03-25 16:08:59 -07:00
|
|
|
|
2023-03-28 01:07:02 -07:00
|
|
|
inline for ([_][]const u8{ "false", "no", "0" }) |candidate| {
|
|
|
|
if (std.mem.eql(u8, comp, candidate)) return false;
|
2023-03-25 16:08:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-28 01:07:02 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}.handler;
|
2023-03-25 16:08:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn string_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
2023-03-28 01:07:02 -07:00
|
|
|
return struct {
|
2023-03-29 23:10:42 -07:00
|
|
|
pub fn handler(_: *gen.UserContext, value: []const u8) ParseError![]const u8 {
|
2023-03-28 01:07:02 -07:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}.handler;
|
2023-03-25 16:08:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn int_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
2023-03-28 23:35:54 -07:00
|
|
|
const IntType = gen.OutputType;
|
2023-03-25 16:08:59 -07:00
|
|
|
|
2023-03-28 01:07:02 -07:00
|
|
|
return struct {
|
2023-03-29 23:10:42 -07:00
|
|
|
pub fn handler(_: *gen.UserContext, value: []const u8) ParseError!IntType {
|
|
|
|
return std.fmt.parseInt(IntType, value, 0) catch return ParseError.ConversionFailed;
|
|
|
|
}
|
|
|
|
}.handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn struct_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|
|
|
const StructType = gen.OutputType;
|
|
|
|
const type_info = @typeInfo(StructType).Struct;
|
|
|
|
const Intermediate = gen.IntermediateType();
|
|
|
|
|
|
|
|
return struct {
|
|
|
|
pub fn handler(context: *gen.UserContext, value: Intermediate) ParseError!StructType {
|
|
|
|
if (value.items.len != type_info.fields.len) return ParseError.ConversionFailed;
|
|
|
|
|
|
|
|
var result: StructType = undefined;
|
|
|
|
inline for (comptime type_info.fields, 0..) |field, idx| {
|
|
|
|
const converter = comptime default_converter(
|
|
|
|
ncmeta.copy_struct(ParameterGenerics, gen, .{
|
|
|
|
.OutputType = field.type,
|
|
|
|
.value_count = .{ .fixed = 1 },
|
|
|
|
}),
|
|
|
|
) orelse
|
|
|
|
@compileError("cannot get converter for field" ++ field.name);
|
|
|
|
|
|
|
|
@field(result, field.name) = try converter(context, value.items[idx]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2023-03-28 01:07:02 -07:00
|
|
|
}
|
|
|
|
}.handler;
|
2023-03-25 16:08:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn choice_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
2023-03-28 23:35:54 -07:00
|
|
|
const EnumType = gen.OutputType;
|
2023-03-25 16:08:59 -07:00
|
|
|
|
2023-03-28 01:07:02 -07:00
|
|
|
return struct {
|
2023-03-29 23:10:42 -07:00
|
|
|
pub fn handler(_: *gen.UserContext, value: []const u8) ParseError!EnumType {
|
|
|
|
return std.meta.stringToEnum(gen.ConvertedType(), value) orelse ParseError.ConversionFailed;
|
2023-03-28 01:07:02 -07:00
|
|
|
}
|
|
|
|
}.handler;
|
2023-03-25 16:08:59 -07:00
|
|
|
}
|