diff options
Diffstat (limited to 'clap')
| -rw-r--r-- | clap/comptime.zig | 14 | ||||
| -rw-r--r-- | clap/streaming.zig | 84 |
2 files changed, 54 insertions, 44 deletions
diff --git a/clap/comptime.zig b/clap/comptime.zig index b0edb2a..e8aae30 100644 --- a/clap/comptime.zig +++ b/clap/comptime.zig | |||
| @@ -6,7 +6,11 @@ const heap = std.heap; | |||
| 6 | const mem = std.mem; | 6 | const mem = std.mem; |
| 7 | const debug = std.debug; | 7 | const debug = std.debug; |
| 8 | 8 | ||
| 9 | pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) type { | 9 | pub fn ComptimeClap( |
| 10 | comptime Id: type, | ||
| 11 | comptime ArgIter: type, | ||
| 12 | comptime params: []const clap.Param(Id), | ||
| 13 | ) type { | ||
| 10 | var flags: usize = 0; | 14 | var flags: usize = 0; |
| 11 | var single_options: usize = 0; | 15 | var single_options: usize = 0; |
| 12 | var multi_options: usize = 0; | 16 | var multi_options: usize = 0; |
| @@ -38,7 +42,7 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) | |||
| 38 | pos: []const []const u8, | 42 | pos: []const []const u8, |
| 39 | allocator: *mem.Allocator, | 43 | allocator: *mem.Allocator, |
| 40 | 44 | ||
| 41 | pub fn parse(allocator: *mem.Allocator, comptime ArgIter: type, iter: *ArgIter) !@This() { | 45 | pub fn parse(allocator: *mem.Allocator, iter: *ArgIter, diag: ?*clap.Diagnostic) !@This() { |
| 42 | var multis = [_]std.ArrayList([]const u8){undefined} ** multi_options; | 46 | var multis = [_]std.ArrayList([]const u8){undefined} ** multi_options; |
| 43 | for (multis) |*multi| { | 47 | for (multis) |*multi| { |
| 44 | multi.* = std.ArrayList([]const u8).init(allocator); | 48 | multi.* = std.ArrayList([]const u8).init(allocator); |
| @@ -58,7 +62,7 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) | |||
| 58 | .params = converted_params, | 62 | .params = converted_params, |
| 59 | .iter = iter, | 63 | .iter = iter, |
| 60 | }; | 64 | }; |
| 61 | while (try stream.next()) |arg| { | 65 | while (try stream.next(diag)) |arg| { |
| 62 | const param = arg.param; | 66 | const param = arg.param; |
| 63 | if (param.names.long == null and param.names.short == null) { | 67 | if (param.names.long == null and param.names.short == null) { |
| 64 | try pos.append(arg.value.?); | 68 | try pos.append(arg.value.?); |
| @@ -143,7 +147,7 @@ pub fn ComptimeClap(comptime Id: type, comptime params: []const clap.Param(Id)) | |||
| 143 | } | 147 | } |
| 144 | 148 | ||
| 145 | test "clap.comptime.ComptimeClap" { | 149 | test "clap.comptime.ComptimeClap" { |
| 146 | const Clap = ComptimeClap(clap.Help, comptime &[_]clap.Param(clap.Help){ | 150 | const Clap = ComptimeClap(clap.Help, clap.args.SliceIterator, comptime &[_]clap.Param(clap.Help){ |
| 147 | clap.parseParam("-a, --aa ") catch unreachable, | 151 | clap.parseParam("-a, --aa ") catch unreachable, |
| 148 | clap.parseParam("-b, --bb ") catch unreachable, | 152 | clap.parseParam("-b, --bb ") catch unreachable, |
| 149 | clap.parseParam("-c, --cc <V>") catch unreachable, | 153 | clap.parseParam("-c, --cc <V>") catch unreachable, |
| @@ -160,7 +164,7 @@ test "clap.comptime.ComptimeClap" { | |||
| 160 | "-a", "-c", "0", "something", "-d", "a", "--dd", "b", | 164 | "-a", "-c", "0", "something", "-d", "a", "--dd", "b", |
| 161 | }, | 165 | }, |
| 162 | }; | 166 | }; |
| 163 | var args = try Clap.parse(&fb_allocator.allocator, clap.args.SliceIterator, &iter); | 167 | var args = try Clap.parse(&fb_allocator.allocator, &iter, null); |
| 164 | defer args.deinit(); | 168 | defer args.deinit(); |
| 165 | 169 | ||
| 166 | testing.expect(args.flag("-a")); | 170 | testing.expect(args.flag("-a")); |
diff --git a/clap/streaming.zig b/clap/streaming.zig index b843bff..90c4e02 100644 --- a/clap/streaming.zig +++ b/clap/streaming.zig | |||
| @@ -24,8 +24,8 @@ pub fn Arg(comptime Id: type) type { | |||
| 24 | pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | 24 | pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { |
| 25 | return struct { | 25 | return struct { |
| 26 | const State = union(enum) { | 26 | const State = union(enum) { |
| 27 | Normal, | 27 | normal, |
| 28 | Chaining: Chaining, | 28 | chaining: Chaining, |
| 29 | 29 | ||
| 30 | const Chaining = struct { | 30 | const Chaining = struct { |
| 31 | arg: []const u8, | 31 | arg: []const u8, |
| @@ -35,49 +35,48 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 35 | 35 | ||
| 36 | params: []const clap.Param(Id), | 36 | params: []const clap.Param(Id), |
| 37 | iter: *ArgIterator, | 37 | iter: *ArgIterator, |
| 38 | state: State = State.Normal, | 38 | state: State = .normal, |
| 39 | 39 | ||
| 40 | /// Get the next ::Arg that matches a ::Param. | 40 | /// Get the next ::Arg that matches a ::Param. |
| 41 | pub fn next(parser: *@This()) !?Arg(Id) { | 41 | pub fn next(parser: *@This(), diag: ?*clap.Diagnostic) !?Arg(Id) { |
| 42 | const ArgInfo = struct { | 42 | const ArgInfo = struct { |
| 43 | const Kind = enum { | ||
| 44 | Long, | ||
| 45 | Short, | ||
| 46 | Positional, | ||
| 47 | }; | ||
| 48 | |||
| 49 | arg: []const u8, | 43 | arg: []const u8, |
| 50 | kind: Kind, | 44 | kind: enum { |
| 45 | long, | ||
| 46 | short, | ||
| 47 | positional, | ||
| 48 | }, | ||
| 51 | }; | 49 | }; |
| 52 | 50 | ||
| 53 | switch (parser.state) { | 51 | switch (parser.state) { |
| 54 | .Normal => { | 52 | .normal => { |
| 55 | const full_arg = (try parser.iter.next()) orelse return null; | 53 | const full_arg = (try parser.iter.next()) orelse return null; |
| 56 | const arg_info = if (mem.eql(u8, full_arg, "--") or mem.eql(u8, full_arg, "-")) | 54 | const arg_info = if (mem.eql(u8, full_arg, "--") or mem.eql(u8, full_arg, "-")) |
| 57 | ArgInfo{ .arg = full_arg, .kind = .Positional } | 55 | ArgInfo{ .arg = full_arg, .kind = .positional } |
| 58 | else if (mem.startsWith(u8, full_arg, "--")) | 56 | else if (mem.startsWith(u8, full_arg, "--")) |
| 59 | ArgInfo{ .arg = full_arg[2..], .kind = .Long } | 57 | ArgInfo{ .arg = full_arg[2..], .kind = .long } |
| 60 | else if (mem.startsWith(u8, full_arg, "-")) | 58 | else if (mem.startsWith(u8, full_arg, "-")) |
| 61 | ArgInfo{ .arg = full_arg[1..], .kind = .Short } | 59 | ArgInfo{ .arg = full_arg[1..], .kind = .short } |
| 62 | else | 60 | else |
| 63 | ArgInfo{ .arg = full_arg, .kind = .Positional }; | 61 | ArgInfo{ .arg = full_arg, .kind = .positional }; |
| 64 | 62 | ||
| 65 | const arg = arg_info.arg; | 63 | const arg = arg_info.arg; |
| 66 | const kind = arg_info.kind; | 64 | const kind = arg_info.kind; |
| 67 | const eql_index = mem.indexOfScalar(u8, arg, '='); | ||
| 68 | 65 | ||
| 69 | switch (kind) { | 66 | switch (kind) { |
| 70 | ArgInfo.Kind.Long => { | 67 | .long => { |
| 68 | const eql_index = mem.indexOfScalar(u8, arg, '='); | ||
| 69 | const name = if (eql_index) |i| arg[0..i] else arg; | ||
| 70 | const maybe_value = if (eql_index) |i| arg[i + 1 ..] else null; | ||
| 71 | |||
| 71 | for (parser.params) |*param| { | 72 | for (parser.params) |*param| { |
| 72 | const match = param.names.long orelse continue; | 73 | const match = param.names.long orelse continue; |
| 73 | const name = if (eql_index) |i| arg[0..i] else arg; | ||
| 74 | const maybe_value = if (eql_index) |i| arg[i + 1 ..] else null; | ||
| 75 | 74 | ||
| 76 | if (!mem.eql(u8, name, match)) | 75 | if (!mem.eql(u8, name, match)) |
| 77 | continue; | 76 | continue; |
| 78 | if (param.takes_value == .None) { | 77 | if (param.takes_value == .None) { |
| 79 | if (maybe_value != null) | 78 | if (maybe_value != null) |
| 80 | return error.DoesntTakeValue; | 79 | return err(diag, param.names, error.DoesntTakeValue); |
| 81 | 80 | ||
| 82 | return Arg(Id){ .param = param }; | 81 | return Arg(Id){ .param = param }; |
| 83 | } | 82 | } |
| @@ -86,19 +85,18 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 86 | if (maybe_value) |v| | 85 | if (maybe_value) |v| |
| 87 | break :blk v; | 86 | break :blk v; |
| 88 | 87 | ||
| 89 | break :blk (try parser.iter.next()) orelse return error.MissingValue; | 88 | break :blk (try parser.iter.next()) orelse |
| 89 | return err(diag, param.names, error.MissingValue); | ||
| 90 | }; | 90 | }; |
| 91 | 91 | ||
| 92 | return Arg(Id){ .param = param, .value = value }; | 92 | return Arg(Id){ .param = param, .value = value }; |
| 93 | } | 93 | } |
| 94 | }, | 94 | }, |
| 95 | ArgInfo.Kind.Short => { | 95 | .short => return try parser.chainging(.{ |
| 96 | return try parser.chainging(State.Chaining{ | 96 | .arg = full_arg, |
| 97 | .arg = full_arg, | 97 | .index = full_arg.len - arg.len, |
| 98 | .index = (full_arg.len - arg.len), | 98 | }, diag), |
| 99 | }); | 99 | .positional => { |
| 100 | }, | ||
| 101 | ArgInfo.Kind.Positional => { | ||
| 102 | for (parser.params) |*param| { | 100 | for (parser.params) |*param| { |
| 103 | if (param.names.long) |_| | 101 | if (param.names.long) |_| |
| 104 | continue; | 102 | continue; |
| @@ -110,13 +108,13 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 110 | }, | 108 | }, |
| 111 | } | 109 | } |
| 112 | 110 | ||
| 113 | return error.InvalidArgument; | 111 | return err(diag, .{ .long = arg }, error.InvalidArgument); |
| 114 | }, | 112 | }, |
| 115 | .Chaining => |state| return try parser.chainging(state), | 113 | .chaining => |state| return try parser.chainging(state, diag), |
| 116 | } | 114 | } |
| 117 | } | 115 | } |
| 118 | 116 | ||
| 119 | fn chainging(parser: *@This(), state: State.Chaining) !?Arg(Id) { | 117 | fn chainging(parser: *@This(), state: State.Chaining, diag: ?*clap.Diagnostic) !?Arg(Id) { |
| 120 | const arg = state.arg; | 118 | const arg = state.arg; |
| 121 | const index = state.index; | 119 | const index = state.index; |
| 122 | const next_index = index + 1; | 120 | const next_index = index + 1; |
| @@ -129,10 +127,10 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 129 | // Before we return, we have to set the new state of the clap | 127 | // Before we return, we have to set the new state of the clap |
| 130 | defer { | 128 | defer { |
| 131 | if (arg.len <= next_index or param.takes_value != .None) { | 129 | if (arg.len <= next_index or param.takes_value != .None) { |
| 132 | parser.state = State.Normal; | 130 | parser.state = .normal; |
| 133 | } else { | 131 | } else { |
| 134 | parser.state = State{ | 132 | parser.state = .{ |
| 135 | .Chaining = State.Chaining{ | 133 | .chaining = .{ |
| 136 | .arg = arg, | 134 | .arg = arg, |
| 137 | .index = next_index, | 135 | .index = next_index, |
| 138 | }, | 136 | }, |
| @@ -144,7 +142,9 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 144 | return Arg(Id){ .param = param }; | 142 | return Arg(Id){ .param = param }; |
| 145 | 143 | ||
| 146 | if (arg.len <= next_index) { | 144 | if (arg.len <= next_index) { |
| 147 | const value = (try parser.iter.next()) orelse return error.MissingValue; | 145 | const value = (try parser.iter.next()) orelse |
| 146 | return err(diag, param.names, error.MissingValue); | ||
| 147 | |||
| 148 | return Arg(Id){ .param = param, .value = value }; | 148 | return Arg(Id){ .param = param, .value = value }; |
| 149 | } | 149 | } |
| 150 | 150 | ||
| @@ -154,7 +154,13 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { | |||
| 154 | return Arg(Id){ .param = param, .value = arg[next_index..] }; | 154 | return Arg(Id){ .param = param, .value = arg[next_index..] }; |
| 155 | } | 155 | } |
| 156 | 156 | ||
| 157 | return error.InvalidArgument; | 157 | return err(diag, .{ .short = arg[index] }, error.InvalidArgument); |
| 158 | } | ||
| 159 | |||
| 160 | fn err(diag: ?*clap.Diagnostic, names: clap.Names, _err: var) @TypeOf(_err) { | ||
| 161 | if (diag) |d| | ||
| 162 | d.name = names; | ||
| 163 | return _err; | ||
| 158 | } | 164 | } |
| 159 | }; | 165 | }; |
| 160 | } | 166 | } |
| @@ -167,7 +173,7 @@ fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, r | |||
| 167 | }; | 173 | }; |
| 168 | 174 | ||
| 169 | for (results) |res| { | 175 | for (results) |res| { |
| 170 | const arg = (c.next() catch unreachable) orelse unreachable; | 176 | const arg = (c.next(null) catch unreachable) orelse unreachable; |
| 171 | testing.expectEqual(res.param, arg.param); | 177 | testing.expectEqual(res.param, arg.param); |
| 172 | const expected_value = res.value orelse { | 178 | const expected_value = res.value orelse { |
| 173 | testing.expectEqual(@as(@TypeOf(arg.value), null), arg.value); | 179 | testing.expectEqual(@as(@TypeOf(arg.value), null), arg.value); |
| @@ -177,7 +183,7 @@ fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, r | |||
| 177 | testing.expectEqualSlices(u8, expected_value, actual_value); | 183 | testing.expectEqualSlices(u8, expected_value, actual_value); |
| 178 | } | 184 | } |
| 179 | 185 | ||
| 180 | if (c.next() catch unreachable) |_| | 186 | if (c.next(null) catch unreachable) |_| |
| 181 | unreachable; | 187 | unreachable; |
| 182 | } | 188 | } |
| 183 | 189 | ||