Skip to content

Commit

Permalink
compiles
Browse files Browse the repository at this point in the history
  • Loading branch information
sam701 committed Oct 11, 2023
1 parent df64f3e commit 03f7c59
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 267 deletions.
92 changes: 34 additions & 58 deletions example/simple.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,38 @@ const cli = @import("zig-cli");
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();

var abc: u16 = undefined;
var abc2: []const u8 = undefined;
var abc3: []u16 = undefined;
var wr = cli.AllocWrapper{ .alloc = allocator };
var config = struct {
host: []const u8 = "localhost",
port: u16 = 3000,
base_value: f64 = undefined,
expose_metrics: bool = false,
}{};

var ip_option = cli.Option{
.long_name = "ip",
.help = "this is the IP address",
.short_alias = 'i',
var host_option = cli.Option{
.long_name = "host",
.help = "host to listen on",
.short_alias = 'h',
.value = cli.OptionValue{ .string = null },
.required = true,
.value_name = "IP",
};
var int_option = cli.Option{
.long_name = "int",
.help = "this is an int",
var port_option = cli.Option{
.long_name = "port",
.help = "post to bind to",
.value = cli.OptionValue{ .int = null },
.value_ref = cli.valueRef(&abc),
// .value_ref2 = wr.sigleInt(&abc),
};
var bool_option = cli.Option{
.long_name = "bool",
.short_alias = 'b',
.help = "this is a bool",
var expose_metrics_option = cli.Option{
.long_name = "expose-metrics",
.short_alias = 'm',
.help = "if the metrics should be exposed",
.value = cli.OptionValue{ .bool = false },
};
var float_option = cli.Option{
.long_name = "float",
.help = "this is a float",
var base_value_option = cli.Option{
.long_name = "base-value",
.help = "base value",
.value = cli.OptionValue{ .float = 0.34 },
};

var name_option = cli.Option{
.long_name = "long_name",
.help = "long_name help",
.value = cli.OptionValue{ .string = null },
};
var app = &cli.App{
.name = "simple",
.description = "This a simple CLI app\nEnjoy!",
Expand All @@ -55,10 +50,10 @@ var app = &cli.App{
\\And this is line 3.
,
.options = &.{
&ip_option,
&int_option,
&bool_option,
&float_option,
&host_option,
&port_option,
&expose_metrics_option,
&base_value_option,
},
.subcommands = &.{
&cli.Command{
Expand All @@ -71,39 +66,20 @@ var app = &cli.App{
};

pub fn main() anyerror!void {
// this works
// var a: u16 = 3;
// var p = cli.IntParser(u16);
// var t = cli.singleValueRef(u16, &a, p, allocator);
// try t.put("56");
// std.debug.print("a = {}\n", .{a});

// var ov = int_option.value_ref.?;
// std.log.info("hello", .{});
// try ov.set("44");
// std.log.debug("value: {}\n", .{abc});

// var ov = int_option;
// _ = ov;
// try ov.put("45");
var ov = try wr.singleInt(&abc);
try ov.put("173");
std.log.debug("value: {}\n", .{abc});

var a = try wr.string(&abc2);
try a.put("hello");
std.log.debug("value: {s}\n", .{abc2});
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
// defer arena.deinit();
const al = arena.allocator();

var a2 = try wr.multiInt(&abc3);
try a2.put("5");
try a2.put("10");
try a2.finalize();
std.log.debug("value: {any}\n", .{abc3});
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);

return cli.run(app, allocator);
}

fn run_sub2(args: []const []const u8) anyerror!void {
var ip = ip_option.value.string.?;
std.log.debug("running sub2: ip={s}, bool={any}, float={any} arg_count={any}", .{ ip, bool_option.value.bool, float_option.value.float, args.len });
var ip = host_option.value.string.?;
std.log.debug("running sub2: ip={s}, bool={any}, float={any} arg_count={any}", .{ ip, expose_metrics_option.value.bool, base_value_option.value.float, args.len });
}
213 changes: 4 additions & 209 deletions src/command.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const std = @import("std");
const vref = @import("./value_ref.zig");
pub const ValueRef = vref.ValueRef;
pub const Context = vref.Context;

pub const App = struct {
name: []const u8,
Expand Down Expand Up @@ -47,220 +50,12 @@ pub const OptionValue = union(enum) {
string_list: ?[]const []const u8,
};

const Setter = *const fn (ptr: *anyopaque, value: []const u8) anyerror!void;

pub const ValueRef = struct {
ptr: *anyopaque,

vtable: *const VTable,

const VTable = struct {
set: Setter,
};

// FIXME: a setter is not enough for multi-value options
/// Deprecated: use ValueRef2
pub fn set(self: *ValueRef, value: []const u8) anyerror!void {
return self.vtable.set(self.ptr, value);
}
};

fn primitiveTypeVTable(comptime T: anytype) *const ValueRef.VTable {
const ptr_info = @typeInfo(T);

std.debug.assert(ptr_info == .Pointer);
std.debug.assert(ptr_info.Pointer.size == .One);
const childT = ptr_info.Pointer.child;
const child_info = @typeInfo(childT);

const setter = switch (child_info) {
.Int => a: {
const gen = struct {
fn setInt(ptr: *anyopaque, value: []const u8) anyerror!void {
var v = try std.fmt.parseInt(childT, value, 10);
const p = @as(*childT, @ptrCast(@alignCast(ptr)));
p.* = v;
}
};
break :a gen.setInt;
},
else => unreachable,
};

const vt = ValueRef.VTable{
.set = setter,
};
return &vt;
}

pub fn valueRef(comptime ptr: anytype) ValueRef {
const ti = @TypeOf(ptr);
const vtable = primitiveTypeVTable(ti);
return .{
.ptr = ptr,
.vtable = vtable,
};
}

pub const Option = struct {
long_name: []const u8,
short_alias: ?u8 = null,
help: []const u8,
required: bool = false,
value: OptionValue,
value_ref: ?ValueRef = null,
value_ref2: ?ValueRef2 = null,
value_ref: ?ValueRef = null, // TODO: remove ?
value_name: []const u8 = "VALUE",
};

pub fn mkOption(comptime ref: anytype, long_name: []const u8, help: []const u8) Option {
return .{
.long_name = long_name,
.help = help,
.value_ref = valueRef(ref),
};
}

pub const ValueRef2 = struct {
impl_ptr: *anyopaque,
vtable: *const VTable,

const Self = @This();

const VTable = struct {
put: *const fn (impl_ptr: *anyopaque, value: []const u8) anyerror!void,

// finalize and destroy
finalize: *const fn (impl_ptr: *anyopaque) anyerror!void,
};

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);
}
};

// const Parser = *const fn (dest: *anyopaque, value: []const u8) anyerror!void;
fn Parser(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 fn IntParser(comptime T: type) Parser(T) {
return struct {
fn parser(dest: *T, value: []const u8) anyerror!void {
var v = try std.fmt.parseInt(T, value, 10);
dest.* = v;
}
}.parser;
}

pub fn StringParser(comptime T: type) Parser(T) {
return struct {
fn parser(dest: *T, value: []const u8) anyerror!void {
dest.* = value;
}
}.parser;
}

fn singleValueRef(comptime T: type, dest: *T, parser: Parser(T), alloc: std.mem.Allocator) !ValueRef2 {
const Impl = struct {
dest: *T,
parser: Parser(T),
alloc: std.mem.Allocator,

const Self = @This();

fn put(ctx: *anyopaque, value: []const u8) anyerror!void {
const self: *Self = @ptrCast(@alignCast(ctx));
try self.parser(self.dest, value);
}
fn finalize(ctx: *anyopaque) anyerror!void {
const self: *Self = @ptrCast(@alignCast(ctx));

// TODO: clarify what to do with copied strings???
self.alloc.destroy(self);
}
};

// FIXME: this must be destroyed
const im = try alloc.create(Impl);
im.* = .{
.dest = dest,
.parser = parser,
.alloc = alloc,
};

return ValueRef2{ .impl_ptr = im, .vtable = &.{
.put = Impl.put,
.finalize = Impl.finalize,
} };
}

// TODO: implement multi-value reference, i.e. ref to a slice/array.
pub fn sliceRef(comptime T: type, dest: *[]T, parser: Parser(T), alloc: std.mem.Allocator) !ValueRef2 {
const List = std.ArrayList(T);
const Impl = struct {
dest: *[]T,
parser: Parser(T),
alloc: std.mem.Allocator,
list: List,

const Self = @This();

fn put(ctx: *anyopaque, value: []const u8) anyerror!void {
const self: *Self = @ptrCast(@alignCast(ctx));
var x: T = undefined;
try self.parser(&x, value);
try self.list.append(x);
}
fn finalize(ctx: *anyopaque) anyerror!void {
const self: *Self = @ptrCast(@alignCast(ctx));
self.dest.* = try self.list.toOwnedSlice();
self.alloc.destroy(self);
}
};

// FIXME: this must be destroyed
const im = try alloc.create(Impl);
im.* = .{
.dest = dest,
.parser = parser,
.alloc = alloc,
.list = List.init(alloc),
};

return ValueRef2{ .impl_ptr = im, .vtable = &.{
.put = Impl.put,
.finalize = Impl.finalize,
} };
}

pub const AllocWrapper = struct {
alloc: std.mem.Allocator,

// TODO: make more generic to guess any value type (out of known), not only int
// TODO: consider parser registration for some type to allow to implement any possible parser
pub fn singleInt(self: *const AllocWrapper, dest: anytype) !ValueRef2 {
const ti = @typeInfo(@TypeOf(dest));
const parser = IntParser(ti.Pointer.child);
return singleValueRef(ti.Pointer.child, dest, parser, self.alloc);
}

pub fn multiInt(self: *const AllocWrapper, dest: anytype) !ValueRef2 {
// const ti = @typeInfo(@TypeOf(dest));
const parser = IntParser(u16);
return sliceRef(u16, dest, parser, self.alloc);
}

pub fn string(self: *const AllocWrapper, dest: anytype) !ValueRef2 {
const ti = @typeInfo(@TypeOf(dest));
const parser = StringParser(ti.Pointer.child);
return singleValueRef(ti.Pointer.child, dest, parser, self.alloc);
}

// TODO: consider arena allocator
// TODO: implement deinit
};
Loading

0 comments on commit 03f7c59

Please sign in to comment.