NOCLIP/source/converters.zig
torque 2c0842f5d4
the wheels look like they're spinning because they are
Had to refactor the multi-value parameter stuff to push some of the key
information in to the generics structure, as they necessarily change
the conversion function signature. Some code has gotten folded
together by being a bit sloppier with inputs and outputs.

This should put us well on our way to having functioning value
conversion, which I think is the main major feature remaining besides
help text generation. Hopefully I won't need to rewrite everything
like this again. While this design seems to be on track to incorporate
all of the main features I am interested in, it has been a lot of work
to wrangle it around, and there is still a lot of work left before I
can put a bow on it.
2023-03-30 00:29:46 -07:00

96 lines
3.3 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 *const fn (gen.UserContext, gen.IntermediateType()) ConversionError!gen.ConvertedType();
}
pub fn FlagConverterSignature(comptime UserContext: type, comptime multi: bool) type {
comptime if (multi)
return *const fn (UserContext, std.ArrayList([]const u8)) ConversionError!std.ArrayList(bool)
else
return *const fn (UserContext, []const u8) ConversionError!bool;
}
pub fn default_converter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
return switch (@typeInfo(gen.OutputType)) {
.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 multi_converter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
// const converter = default_converter(gen) orelse @compileError("no default converter");
// return struct {
// pub fn handler(_: UserContext, input: std.ArrayList([]const u8)) ConversionError!std.ArrayList(OutputType) {
// var output = std.ArrayList(OutputType).initCapacity(input.allocator, input.items.len) catch return ConversionError.BadValue;
// for (input.items) |item| {
// output.appendAssumeCapacity()
// }
// }
// }.handler;
// }
fn flag_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
return struct {
pub fn handler(_: gen.UserContext, 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 struct {
pub fn handler(_: gen.UserContext, value: []const u8) ConversionError![]const u8 {
return value;
}
}.handler;
}
fn int_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
const IntType = gen.OutputType;
comptime std.debug.assert(@typeInfo(IntType) == .Int);
return struct {
pub fn handler(_: gen.UserContext, 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.OutputType;
return struct {
pub fn handler(_: gen.UserContext, value: []const u8) ConversionError!EnumType {
return std.meta.stringToEnum(gen.ConvertedType(), value) orelse ConversionError.BadValue;
}
}.handler;
}