Skip to content

Commit 9854c80

Browse files
committed
Pushing soem current fixes as we work on solving the comptime issues
1 parent 3739f7e commit 9854c80

File tree

2 files changed

+45
-27
lines changed

2 files changed

+45
-27
lines changed

src/main.zig

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ pub fn main() !void {
1616
const parsed_cli = try cli.parse();
1717

1818
// Necessary is skipped here
19-
std.debug.print("{s} {d} {any} {s} {s}", .{ parsed_cli.name, parsed_cli.location, parsed_cli.exists, parsed_cli.default_name, if (parsed_cli.filled_optional) |filled| filled orelse "badvalue" });
19+
std.debug.print("{s} {d} {any} {s} {s}", .{ parsed_cli.name, parsed_cli.location, parsed_cli.exists, parsed_cli.default_name, parsed_cli.filled_optional orelse "badvalue" });
2020
}

src/sneaky.zig

+44-26
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const std = @import("std");
44
const builtin = @import("builtin");
55
const assert = std.debug.assert;
66

7-
pub const CliError = error{ InvalidArg, InvalidNumberOfArgs, CliArgumentNotFound, InvalidCommand, HelpCommand, IncorrectArgumentType, RequiredArgumentNotFound };
7+
pub const CliError = error{ InvalidArg, InvalidNumberOfArgs, CliArgumentNotFound, InvalidCommand, HelpCommand, IncorrectArgumentType, RequiredArgumentNotFound, UnexpectedCliType, NonStructPassed };
88

99
const ArgMetadata = struct {
1010
key: []const u8,
@@ -99,7 +99,7 @@ pub fn Snek(comptime CliInterface: type) type {
9999
pub fn parse(self: *Self) CliError!CliInterface {
100100
if (!self.isStruct()) {
101101
std.debug.print("Struct must be passed to parse function. Type: {any} found", .{@TypeOf(CliInterface)});
102-
return;
102+
return CliError.NonStructPassed;
103103
}
104104

105105
const interface: CliInterface = undefined;
@@ -135,38 +135,44 @@ pub fn Snek(comptime CliInterface: type) type {
135135
continue :unwrap_for;
136136
},
137137
else => {
138+
// Check if there is a default value, if there is, move on (same case as an optional). Else, error case
139+
if (field.default_value) continue :unwrap_for;
140+
138141
std.debug.print("Required arugment {s} was not found in CLI flags. Check -help menu for required flags", .{field.name});
139142
return CliError.RequiredArgumentNotFound;
140143
},
141144
}
142145
}
143146

144-
// Write data to struct field based on typ witin arg. Arg, at this point, should never be null
147+
// Write data to struct field based on typ witin arg. Arg, at this point, should never be null since we capture that case above
145148
const serialized_arg = arg.?;
146149
switch (@typeInfo(serialized_arg.typ)) {
147150
.Bool => {
148-
@field(&cli_reflected, serialized_arg.key) = try self.parseBool(serialized_arg.key);
151+
@field(&interface, serialized_arg.key) = try self.parseBool(serialized_arg.key);
149152
},
150153
.Int => {
151-
@field(&cli_reflected, serialized_arg.key) = try self.parseBool(serialized_arg.key);
154+
@field(&interface, serialized_arg.key) = try self.parseNumeric(serialized_arg.key);
152155
},
153156
.Float => {
154-
@field(&cli_reflected, serialized_arg.key) = try self.parseBool(serialized_arg.key);
157+
@field(&interface, serialized_arg.key) = try self.parseNumeric(serialized_arg.key);
155158
},
156159
.Pointer => {
157160
// .Pointer is for strings since the underlying type is []const u8 which is a .Pointer type
158-
if (serialized_arg.typ.Pointer.size == .Slice and serialized_arg.typ.Pointer.child == u8) {}
161+
if (serialized_arg.typ.Pointer.size == .Slice and serialized_arg.typ.Pointer.child == u8) {
162+
// At this point, just store the string.
163+
@field(&interface, serialized_arg.key) = serialized_arg.key;
164+
}
165+
},
166+
.Struct => {
167+
return CliError.UnexpectedCliType;
159168
},
160-
.Struct => {},
161169
else => {
162-
@panic("unexpected type received in CLI parser");
170+
return CliError.UnexpectedCliType;
163171
},
164172
}
165173
}
166174

167-
// Check that all struct fields are set and we are not missing any required fields. If we are, error.
168-
// Track which fields are missing as well so we can reflect that back to the user
169-
try self.checkAllStructArgs(cli_reflected);
175+
return interface;
170176
}
171177

172178
// ## Helper Functions ##
@@ -186,12 +192,12 @@ pub fn Snek(comptime CliInterface: type) type {
186192
// Remove the - without calling std.mem
187193
const arg_stripped = arg[1..];
188194

189-
// Help command is treated as an exit case to display the help menu. This is the same way that Go does it in Flags
195+
// Help command is treated as an exit case to display the help menu. This is the same way that Go does it in the Flag package
190196
// https://cs.opensource.google/go/go/+/refs/tags/go1.23.1:src/flag/flag.go;l=1111
191-
if (std.mem.eql(arg_stripped, "help") or std.mem.eql(u8, arg_stripped, "h")) return CliError.HelpCommand;
197+
if (std.mem.eql(u8, arg_stripped, "help") or std.mem.eql(u8, arg_stripped, "h")) return CliError.HelpCommand;
192198

193199
// Split on all data *after* the initial - and curate a roster of key/value arguments seerated by the =
194-
const split_arg = std.mem.split(u8, arg_stripped, "=");
200+
var split_arg = std.mem.split(u8, arg_stripped, "=");
195201
const arg_key = split_arg.next() orelse "";
196202
const arg_val = split_arg.next() orelse "";
197203

@@ -207,10 +213,10 @@ pub fn Snek(comptime CliInterface: type) type {
207213
}
208214

209215
// No struct field of this name was found. Send error instead of moving on
210-
if (!self.checkForKey(arg_key_d)) CliError.InvalidCommand;
216+
if (!self.checkForKey(arg_key_d)) return CliError.InvalidCommand;
211217

212218
// .typ is used to eventually switch when we marshal the type of the value into the struct field
213-
try self.arg_metadata.put(arg_key_d, .{ .key = arg_key_d, .value = arg_val_d, .optional = self.isOptional(arg_key_d), .typ = extractTypeInfoFromKey(arg_key_d) });
219+
try self.arg_metadata.put(arg_key_d, .{ .key = arg_key_d, .value = std.mem.trim(u8, arg_val_d, " "), .optional = self.isOptional(arg_key_d), .typ = extractTypeInfoFromKey(arg_key_d) });
214220
}
215221
}
216222

@@ -219,12 +225,6 @@ pub fn Snek(comptime CliInterface: type) type {
219225
return @hasField(CliInterface, key);
220226
}
221227

222-
/// Check all fields of the struct to ensure that all non-optional values are set and non are missing
223-
fn checkAllStructArgs(self: *Self, comptime T: type) CliError!void {
224-
_ = self;
225-
_ = T;
226-
}
227-
228228
fn extractTypeInfoFromKey(key: []const u8) std.builtin.Type {
229229
const s_enum = std.meta.stringToEnum(CliInterface, key);
230230

@@ -257,16 +257,34 @@ pub fn Snek(comptime CliInterface: type) type {
257257
}
258258

259259
// ## Parser Functions ##
260+
260261
fn parseBool(self: Self, parse_value: []const u8) !void {
261262
_ = self;
262-
_ = parse_value;
263+
if (std.mem.eql(u8, parse_value, "True") or std.mem.eql(u8, parse_value, "true") or std.mem.eql(u8, parse_value, "On") or std.mem.eql(u8, parse_value, "on")) {
264+
return true;
265+
} else if (std.mem.eql(u8, parse_value, "False") or std.mem.eql(u8, parse_value, "false") or std.mem.eql(u8, parse_value, "Off") or std.mem.eql(u8, parse_value, "off")) {
266+
return false;
267+
}
268+
269+
return error.NotBoolean;
263270
}
264271

265-
fn parseNumeric(self: Self, parse_value: []const u8) !void {
272+
fn parseNumeric(self: Self, comptime T: type, parse_value: []const u8) !void {
266273
_ = self;
267-
_ = parse_value;
274+
switch (@typeInfo(T)) {
275+
.Int => {
276+
return std.fmt.parseInt(T, parse_value, 10);
277+
},
278+
.Float => {
279+
return std.fmt.parseFloat(T, parse_value);
280+
},
281+
else => {
282+
return error.UnrecognizedSimpleType;
283+
},
284+
}
268285
}
269286

287+
// Unused - Keep around in case of more advanced string parsing
270288
fn parseString(self: Self, parse_value: []const u8) !void {
271289
_ = self;
272290
_ = parse_value;

0 commit comments

Comments
 (0)