diff --git a/example/simple.zig b/example/simple.zig index 7fb8cd5..5be8a22 100644 --- a/example/simple.zig +++ b/example/simple.zig @@ -9,6 +9,7 @@ var config = struct { port: u16 = 3000, base_value: f64 = undefined, expose_metrics: bool = false, + testar: []i32 = undefined, }{}; var host_option = cli.Option{ @@ -16,6 +17,7 @@ var host_option = cli.Option{ .help = "host to listen on", .short_alias = 'h', .value = cli.OptionValue{ .string = null }, + .value_ref = cli.valueRef(&config.host), .required = true, .value_name = "IP", }; @@ -23,6 +25,13 @@ var port_option = cli.Option{ .long_name = "port", .help = "post to bind to", .value = cli.OptionValue{ .int = null }, + .value_ref = cli.valueRef(&config.port), +}; +var ar_option = cli.Option{ + .long_name = "array", + .help = "slice test", + .value = cli.OptionValue{ .int = null }, + .value_ref = cli.valueRef(&config.testar), }; var expose_metrics_option = cli.Option{ .long_name = "expose-metrics", @@ -66,16 +75,17 @@ var app = &cli.App{ }; pub fn main() anyerror!void { - var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); - // defer arena.deinit(); - const al = arena.allocator(); + try port_option.value_ref.?.put("356", allocator); + std.log.debug("port value = {}", .{config.port}); - var ctx = cli.Context.init(al); - host_option.value_ref = try ctx.valueRef(&config.host); - port_option.value_ref = try ctx.valueRef(&config.port); - expose_metrics_option.value_ref = try ctx.valueRef(&config.expose_metrics); - base_value_option.value_ref = try ctx.valueRef(&config.base_value); + try host_option.value_ref.?.put("awesome.com", allocator); + std.log.debug("host value = {s}", .{config.host}); + try ar_option.value_ref.?.put("45", allocator); + try ar_option.value_ref.?.put("94", allocator); + try ar_option.value_ref.?.put("23456789", allocator); + try ar_option.value_ref.?.finalize(allocator); + std.log.debug("testar value = {any}", .{config.testar}); return cli.run(app, allocator); } diff --git a/src/command.zig b/src/command.zig index 2ccda07..35b0780 100644 --- a/src/command.zig +++ b/src/command.zig @@ -1,7 +1,7 @@ const std = @import("std"); const vref = @import("./value_ref.zig"); pub const ValueRef = vref.ValueRef; -pub const Context = vref.Context; +pub const valueRef = vref.valueRef; pub const App = struct { name: []const u8, diff --git a/src/value_parser.zig b/src/value_parser.zig index c030179..37a7dcd 100644 --- a/src/value_parser.zig +++ b/src/value_parser.zig @@ -1,53 +1,70 @@ const std = @import("std"); -// const Parser = *const fn (dest: *anyopaque, value: []const u8) anyerror!void; -pub fn ValueParser(comptime T: type) type { - // TODO: the parse function might need an allocator, e.g. to copy a string or allocate the destination type if it is a pointer. - return *const fn (dest: *T, value: []const u8) anyerror!void; -} +pub const ValueParser = *const fn (dest: *anyopaque, value: []const u8) anyerror!void; + +pub const ValueData = struct { + value_size: usize, + value_parser: ValueParser, +}; -pub fn get(comptime T: type) ValueParser(T) { +pub fn getValueData(comptime T: type) ValueData { return switch (@typeInfo(T)) { - .Int => intParser(T), - .Float => floatParser(T), - .Bool => boolParser(T), + .Int => intData(T), + .Float => floatData(T), + .Bool => boolData(T), .Pointer => |pinfo| { if (pinfo.size == .Slice and pinfo.child == u8) { - stringParser(T); + return stringData(T); } }, else => @compileError("unsupported value type"), }; } -fn intParser(comptime T: type) ValueParser(T) { - return struct { - fn parser(dest: *T, value: []const u8) anyerror!void { - dest.* = try std.fmt.parseInt(T, value, 10); - } - }.parser; +fn intData(comptime T: type) ValueData { + return .{ + .value_size = @sizeOf(T), + .value_parser = struct { + fn parser(dest: *anyopaque, value: []const u8) anyerror!void { + const dt: *T = @ptrCast(@alignCast(dest)); + dt.* = try std.fmt.parseInt(T, value, 10); + } + }.parser, + }; } -fn floatParser(comptime T: type) ValueParser(T) { - return struct { - fn parser(dest: *T, value: []const u8) anyerror!void { - dest.* = try std.fmt.parseFloat(T, value); - } - }.parser; +fn floatData(comptime T: type) ValueData { + return .{ + .value_size = @sizeOf(T), + .value_parser = struct { + fn parser(dest: *anyopaque, value: []const u8) anyerror!void { + const dt: *T = @ptrCast(@alignCast(dest)); + dt.* = try std.fmt.parseFloat(T, value); + } + }.parser, + }; } -fn boolParser(comptime T: type) ValueParser(T) { - return struct { - fn parser(dest: *T, value: []const u8) anyerror!void { - dest.* = std.mem.eql(u8, value, "true"); - } - }.parser; +fn boolData(comptime T: type) ValueData { + return .{ + .value_size = @sizeOf(T), + .value_parser = struct { + fn parser(dest: *anyopaque, value: []const u8) anyerror!void { + const dt: *T = @ptrCast(@alignCast(dest)); + dt.* = std.mem.eql(u8, value, "true"); + } + }.parser, + }; } -fn stringParser(comptime T: type) ValueParser(T) { - return struct { - fn parser(dest: *T, value: []const u8) anyerror!void { - dest.* = value; - } - }.parser; +fn stringData(comptime T: type) ValueData { + return .{ + .value_size = @sizeOf(T), + .value_parser = struct { + fn parser(dest: *anyopaque, value: []const u8) anyerror!void { + const dt: *T = @ptrCast(@alignCast(dest)); + dt.* = value; + } + }.parser, + }; } diff --git a/src/value_ref.zig b/src/value_ref.zig index 626da99..8e71fcb 100644 --- a/src/value_ref.zig +++ b/src/value_ref.zig @@ -3,130 +3,81 @@ const command = @import("./command.zig"); const vp = @import("./value_parser.zig"); pub const ValueRef = struct { - impl_ptr: *anyopaque, - vtable: *const VTable, + dest: *anyopaque, + value_data: vp.ValueData, + value_type: ValueType, + element_count: usize = 0, const Self = @This(); - const VTable = struct { - put: *const fn (impl_ptr: *anyopaque, value: []const u8) anyerror!void, + pub fn put(self: *Self, value: []const u8, alloc: std.mem.Allocator) anyerror!void { + switch (self.value_type) { + .single => { + return self.value_data.value_parser(self.dest, value); + }, + .multi => |*list| { + self.element_count += 1; + try list.ensureTotalCapacity(alloc, self.element_count * self.value_data.value_size); + const value_ptr = list.items.ptr + ((self.element_count - 1) * self.value_data.value_size); + try self.value_data.value_parser(value_ptr, value); + list.items.len += self.value_data.value_size; + }, + } + } - /// finalize and destroy - finalize: *const fn (impl_ptr: *anyopaque) anyerror!void, - }; + pub fn finalize(self: *Self, alloc: std.mem.Allocator) anyerror!void { + switch (self.value_type) { + .single => {}, + .multi => |*list| { + var sl = try list.toOwnedSlice(alloc); + sl.len = self.element_count; - pub fn put(self: *Self, value: []const u8) anyerror!void { - return self.vtable.put(self.impl_ptr, value); - } - pub fn finalize(self: *Self) anyerror!void { - return self.vtable.finalize(self.impl_ptr); + var dest: *[]u8 = @alignCast(@ptrCast(self.dest)); + dest.* = sl; + }, + } } }; -// TODO: can ValueRef be an enum???? +const ValueType = union(enum) { + single, + multi: std.ArrayListUnmanaged(u8), +}; const AllocError = std.mem.Allocator.Error; pub const Error = AllocError; // | error{NotImplemented}; -pub const Context = struct { - alloc: std.mem.Allocator, - - pub fn init(a: std.mem.Allocator) Context { - return .{ - .alloc = a, - }; - } - - fn singleValueRef(ctx: *Context, comptime T: type, dest: *T, parser: vp.ValueParser(T)) AllocError!ValueRef { - const Impl = struct { - dest: *T, - parser: vp.ValueParser(T), - alloc: std.mem.Allocator, - - const Self = @This(); - - fn put(ptr: *anyopaque, value: []const u8) anyerror!void { - const self: *Self = @ptrCast(@alignCast(ptr)); - try self.parser(self.dest, value); - } - fn finalize(ptr: *anyopaque) anyerror!void { - const self: *Self = @ptrCast(@alignCast(ptr)); - - // TODO: clarify what to do with copied strings??? - self.alloc.destroy(self); - } - }; - - // FIXME: this must be destroyed - const im = try ctx.alloc.create(Impl); - im.* = .{ - .dest = dest, - .parser = parser, - .alloc = ctx.alloc, - }; - - return ValueRef{ .impl_ptr = im, .vtable = &.{ - .put = Impl.put, - .finalize = Impl.finalize, - } }; - } - - pub fn sliceRef(ctx: *Context, comptime T: type, dest: *[]const T, parser: vp.ValueParser(T)) AllocError!ValueRef { - const List = std.ArrayList(T); - const Impl = struct { - dest: *[]const T, - parser: vp.ValueParser(T), - list: List, - alloc2: std.mem.Allocator, - - const Self = @This(); - - fn put(ptr: *anyopaque, value: []const u8) anyerror!void { - const self: *Self = @ptrCast(@alignCast(ptr)); - var x: T = undefined; - try self.parser(&x, value); - try self.list.append(x); +pub fn valueRef(dest: anytype) ValueRef { + const ti = @typeInfo(@TypeOf(dest)); + const t = ti.Pointer.child; + + switch (@typeInfo(t)) { + .Pointer => |pinfo| { + switch (pinfo.size) { + .Slice => { + if (pinfo.child == u8) { + return ValueRef{ + .dest = @ptrCast(dest), + .value_data = vp.getValueData(t), + .value_type = .single, + }; + } else { + return ValueRef{ + .dest = @ptrCast(dest), + .value_data = vp.getValueData(pinfo.child), + .value_type = ValueType{ .multi = std.ArrayListUnmanaged(u8){} }, + }; + } + }, + else => @compileError("unsupported value type"), } - fn finalize(ptr: *anyopaque) anyerror!void { - const self: *Self = @ptrCast(@alignCast(ptr)); - self.dest.* = try self.list.toOwnedSlice(); - self.alloc2.destroy(self); - } - }; - - // FIXME: this must be destroyed - const im = try ctx.alloc.create(Impl); - im.* = .{ - .dest = dest, - .parser = parser, - .list = List.init(ctx.alloc), - .alloc2 = ctx.alloc, - }; - - return ValueRef{ .impl_ptr = im, .vtable = &.{ - .put = Impl.put, - .finalize = Impl.finalize, - } }; - } - - pub fn valueRef(ctx: *Context, comptime dest: anytype) Error!ValueRef { - const ti = @typeInfo(@TypeOf(dest)); - const t = ti.Pointer.child; - - switch (@typeInfo(t)) { - .Pointer => |pinfo| { - switch (pinfo.size) { - .Slice => { - const p = vp.get(pinfo.child); - return ctx.sliceRef(pinfo.child, dest, p); - }, - else => @compileError("unsupported value type"), - } - }, - else => { - const p = vp.get(t); - return ctx.singleValueRef(t, dest, p); - }, - } + }, + else => { + return ValueRef{ + .dest = @ptrCast(dest), + .value_data = vp.getValueData(t), + .value_type = .single, + }; + }, } -}; +}