Skip to content

Commit

Permalink
It all works now
Browse files Browse the repository at this point in the history
  • Loading branch information
BitlyTwiser committed Sep 23, 2024
1 parent 49c6d64 commit def2779
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 44 deletions.
2 changes: 1 addition & 1 deletion src/main.zig
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ pub fn main() !void {
const parsed_cli = try cli.parse();

// Necessary is skipped here to showcase optional values being ignored
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" });
std.debug.print("Name: {s}\n Location: {d}\n Exists: {any}\n Defualt value: {s}\n Filled Optional: {s}\n", .{ parsed_cli.name, parsed_cli.location, parsed_cli.exists, parsed_cli.default_name, parsed_cli.filled_optional orelse "badvalue" });
}
93 changes: 50 additions & 43 deletions src/sneaky.zig
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub fn Snek(comptime CliInterface: type) type {
}

pub fn deinit(self: *Self) void {
_ = self;
self.arg_metadata.clearAndFree();
}

/// deinitMem deinitializes abitrary memory
Expand Down Expand Up @@ -124,59 +124,67 @@ pub fn Snek(comptime CliInterface: type) type {
}
};

unwrap_for: inline for (cli_reflected.Struct.fields) |field| {
inline for (cli_reflected.Struct.fields) |field| {
const arg = self.arg_metadata.get(field.name) orelse null;

// If arg does NOT exist and the field is NOT optional, its an error case, so handle accordingly
if (arg == null) {
switch (@typeInfo(field.type)) {
.Optional => {
break :unwrap_for;
// If this is an optional and there is no default value, set field value to null entirely (its an optional after all)
if (field.default_value == null) {
@field(&interface, field.name) = null;
}
},
else => {
// Check if there is a default value, if there is, move on (same case as an optional). Else, error case
if (field.default_value == null) break :unwrap_for;

std.debug.print("Required arugment {s} was not found in CLI flags. Check -help menu for required flags", .{field.name});
return CliError.RequiredArgumentNotFound;
// Check if there is a default value, if there is, move on (same case as an optional). Else, error case since its required and has no value
if (field.default_value == null) {
std.debug.print("Required arugment {s} was not found in CLI flags. Check -help menu for required flags", .{field.name});
return CliError.RequiredArgumentNotFound;
} else {
// Actually extract the value
const dvalue_aligned: *align(field.alignment) const anyopaque = @alignCast(field.default_value.?);
const value = @as(*const field.type, @ptrCast(dvalue_aligned)).*;
@field(&interface, field.name) = value;
}
},
}
}

// Write data to struct field based on typ witin arg. Arg, at this point, should never be null since we capture that case above
comptime var field_type: std.builtin.Type = undefined;
const serialized_arg = arg.?;
// handle child case of optional type to get true base type for optional support
if (@typeInfo(field.type) == .Optional) {
const i = @typeInfo(field.type);
field_type = @typeInfo(i.Optional.child);
} else {
field_type = @typeInfo(field.type);
}
// Write data to struct field based on typ witin arg. Arg, at this point, should never be null since we capture that case above
comptime var field_type: std.builtin.Type = undefined;
const serialized_arg = arg.?;
// handle child case of optional type to get true base type for optional support
if (@typeInfo(field.type) == .Optional) {
const i = @typeInfo(field.type);
field_type = @typeInfo(i.Optional.child);
} else {
field_type = @typeInfo(field.type);
}

switch (field_type) {
.Bool => {
@field(&interface, field.name) = try self.parseBool(serialized_arg.value);
},
.Int => {
@field(&interface, field.name) = try self.parseNumeric(field.type, serialized_arg.value);
},
.Float => {
@field(&interface, field.name) = try self.parseNumeric(field.type, serialized_arg.value);
},
.Pointer => {
// .Pointer is for strings since the underlying type is []const u8 which is a .Pointer type
if (field_type.Pointer.size == .Slice and field_type.Pointer.child == u8) {
// At this point, just store the string.
@field(&interface, field.name) = serialized_arg.value;
}
},
.Struct => {
return CliError.UnexpectedCliType;
},
else => {
return CliError.UnexpectedCliType;
},
switch (field_type) {
.Bool => {
@field(&interface, field.name) = try self.parseBool(serialized_arg.value);
},
.Int => {
@field(&interface, field.name) = try self.parseNumeric(field.type, serialized_arg.value);
},
.Float => {
@field(&interface, field.name) = try self.parseNumeric(field.type, serialized_arg.value);
},
.Pointer => {
// .Pointer is for strings since the underlying type is []const u8 which is a .Pointer type
if (field_type.Pointer.size == .Slice and field_type.Pointer.child == u8) {
// At this point, just store the string.
@field(&interface, field.name) = serialized_arg.value;
}
},
.Struct => {
return CliError.UnexpectedCliType;
},
else => {
return CliError.UnexpectedCliType;
},
}
}
}

Expand All @@ -187,7 +195,6 @@ pub fn Snek(comptime CliInterface: type) type {

fn collectArgs(self: *Self) !void {
var args = try std.process.argsWithAllocator(self.allocator);
defer deinit(self);

// Skip first line, its always the name of the calling function
_ = args.skip();
Expand Down

0 comments on commit def2779

Please sign in to comment.