summaryrefslogtreecommitdiff
path: root/src/extended.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/extended.zig')
-rw-r--r--src/extended.zig233
1 files changed, 0 insertions, 233 deletions
diff --git a/src/extended.zig b/src/extended.zig
deleted file mode 100644
index f7fc87d..0000000
--- a/src/extended.zig
+++ /dev/null
@@ -1,233 +0,0 @@
1pub const core = @import("core.zig");
2
3const builtin = @import("builtin");
4const std = @import("std");
5
6const mem = std.mem;
7const fmt = std.fmt;
8const debug = std.debug;
9const io = std.io;
10
11const assert = debug.assert;
12
13pub const Param = struct {
14 field: []const u8,
15 names: core.Names,
16 kind: Kind,
17
18 required: bool,
19 position: ?usize,
20
21 pub fn flag(field: []const u8, names: core.Names) Param {
22 return init(
23 field,
24 names,
25 Kind.Flag,
26 );
27 }
28
29 pub fn option(
30 field: []const u8,
31 names: core.Names,
32 comptime parser: Parser,
33 ) Param {
34 return init(
35 field,
36 names,
37 Kind{ .Option = parser },
38 );
39 }
40
41 pub fn subcommand(
42 field: []const u8,
43 names: core.Names,
44 comptime command: Command,
45 ) Param {
46 return init(
47 field,
48 names,
49 Kind{ .Subcommand = command },
50 );
51 }
52
53 pub fn init(field: []const u8, names: core.Names, kind: Kind) Param {
54 return Param{
55 .field = field,
56 .names = names,
57 .kind = kind,
58 .required = false,
59 .position = null,
60 };
61 }
62
63 pub const Kind = union(enum) {
64 Flag,
65 Option: Parser,
66 Subcommand: Command,
67 };
68};
69
70const Opaque = @OpaqueType();
71pub const Command = struct {
72 params: []const Param,
73
74 Result: type,
75 default: *const Opaque,
76
77 pub fn init(comptime Result: type, default: *const Result, params: []const Param) Command {
78 return Command{
79 .params = params,
80 .Result = Result,
81 .default = @ptrCast(*const Opaque, default),
82 };
83 }
84};
85
86pub const Parser = struct {
87 const UnsafeFunction = *const void;
88
89 FieldType: type,
90 Errors: type,
91 func: UnsafeFunction,
92
93 pub fn init(comptime FieldType: type, comptime Errors: type, func: ParseFunc(FieldType, Errors)) Parser {
94 return Parser{
95 .FieldType = FieldType,
96 .Errors = Errors,
97 .func = @ptrCast(UnsafeFunction, func),
98 };
99 }
100
101 fn parse(comptime parser: Parser, field_ptr: *parser.FieldType, arg: []const u8) parser.Errors!void {
102 return @ptrCast(ParseFunc(parser.FieldType, parser.Errors), parser.func)(field_ptr, arg);
103 }
104
105 fn ParseFunc(comptime FieldType: type, comptime Errors: type) type {
106 return fn (*FieldType, []const u8) Errors!void;
107 }
108
109 pub fn int(comptime Int: type, comptime radix: u8) Parser {
110 const func = struct {
111 fn i(field_ptr: *Int, arg: []const u8) !void {
112 field_ptr.* = try fmt.parseInt(Int, arg, radix);
113 }
114 }.i;
115 return Parser.init(Int, @typeOf(func).ReturnType.ErrorSet, func);
116 }
117
118 const string = Parser.init([]const u8, error{}, struct {
119 fn s(field_ptr: *[]const u8, arg: []const u8) (error{}!void) {
120 field_ptr.* = arg;
121 }
122 }.s);
123};
124
125pub fn Clap(comptime Result: type) type {
126 return struct {
127 const Self = this;
128
129 default: Result,
130 params: []const Param,
131
132 // TODO: pass-by-value
133 pub fn parse(
134 comptime clap: *const Self,
135 comptime Error: type,
136 iter: *core.ArgIterator(Error),
137 ) !Result {
138 // We initialize the core.Clap without any params, and fill them out in parseHelper.
139 var c = core.Clap(usize, Error).init([]core.Param(usize){}, iter);
140
141 const top_level_command = comptime Command.init(Result, &clap.default, clap.params);
142 return try parseHelper(&top_level_command, Error, &c);
143 }
144
145 // TODO: pass-by-value
146 fn parseHelper(
147 comptime command: *const Command,
148 comptime Error: type,
149 clap: *core.Clap(usize, Error),
150 ) !command.Result {
151 var result = @ptrCast(*const command.Result, command.default).*;
152
153 var handled = comptime blk: {
154 var res: [command.params.len]bool = undefined;
155 for (command.params) |p, i| {
156 res[i] = !p.required;
157 }
158
159 break :blk res;
160 };
161
162 // We replace the current clap with the commands parameters, so that we preserve the that
163 // claps state. This is important, as core.Clap could be in a Chaining state, and
164 // constructing a new core.Clap would skip the last chaining arguments.
165 clap.params = comptime blk: {
166 var res: [command.params.len]core.Param(usize) = undefined;
167
168 for (command.params) |p, i| {
169 const id = i;
170 res[id] = core.Param(usize){
171 .id = id,
172 .takes_value = p.kind == Param.Kind.Option,
173 .names = p.names,
174 };
175 }
176
177 break :blk res;
178 };
179
180 var pos: usize = 0;
181
182 arg_loop: while (try clap.next()) |arg| : (pos += 1) {
183 inline for (command.params) |param, i| {
184 if (arg.param.id == i and (param.position orelse pos) == pos) {
185 handled[i] = true;
186
187 switch (param.kind) {
188 Param.Kind.Flag => {
189 getFieldPtr(&result, param.field).* = true;
190 },
191 Param.Kind.Option => |parser| {
192 try parser.parse(getFieldPtr(&result, param.field), arg.value.?);
193 },
194 Param.Kind.Subcommand => |sub_command| {
195 getFieldPtr(&result, param.field).* = try sub_command.parseHelper(Error, clap);
196
197 // After parsing a subcommand, there should be no arguments left.
198 break :arg_loop;
199 },
200 }
201 continue :arg_loop;
202 }
203 }
204
205 return error.InvalidArgument;
206 }
207
208 for (handled) |h| {
209 if (!h)
210 return error.ParamNotHandled;
211 }
212
213 return result;
214 }
215
216 fn GetFieldPtrReturn(comptime Struct: type, comptime field: []const u8) type {
217 var inst: Struct = undefined;
218 const dot_index = comptime mem.indexOfScalar(u8, field, '.') orelse {
219 return @typeOf(&@field(inst, field));
220 };
221
222 return GetFieldPtrReturn(@typeOf(@field(inst, field[0..dot_index])), field[dot_index + 1 ..]);
223 }
224
225 fn getFieldPtr(curr: var, comptime field: []const u8) GetFieldPtrReturn(@typeOf(curr).Child, field) {
226 const dot_index = comptime mem.indexOfScalar(u8, field, '.') orelse {
227 return &@field(curr, field);
228 };
229
230 return getFieldPtr(&@field(curr, field[0..dot_index]), field[dot_index + 1 ..]);
231 }
232 };
233}