feature. See also
. The project being documented here (as the example) is the Zig library itself.
cli.Arg
pub const Arg = struct
File
Code
pub const Arg = struct {
prefix: enum { long, short, slash },
name_offset: usize,
full: []const u8,
pub fn fromString(str: []const u8) ?@This() {
if (std.mem.startsWith(u8, str, "--")) {
return .{ .prefix = .long, .name_offset = 2, .full = str };
} else if (std.mem.startsWith(u8, str, "-")) {
return .{ .prefix = .short, .name_offset = 1, .full = str };
} else if (std.mem.startsWith(u8, str, "/")) {
return .{ .prefix = .slash, .name_offset = 1, .full = str };
}
return null;
}
pub fn prefixSlice(self: Arg) []const u8 {
return self.full[0..(if (self.prefix == .long) 2 else 1)];
}
pub fn name(self: Arg) []const u8 {
return self.full[self.name_offset..];
}
pub fn optionWithoutPrefix(self: Arg, option_len: usize) []const u8 {
if (option_len == 0) return self.name();
return self.name()[0..option_len];
}
pub fn missingSpan(self: Arg) Diagnostics.ErrorDetails.ArgSpan {
return .{
.point_at_next_arg = true,
.value_offset = 0,
.name_offset = self.name_offset,
.prefix_len = self.prefixSlice().len,
};
}
pub fn optionAndAfterSpan(self: Arg) Diagnostics.ErrorDetails.ArgSpan {
return self.optionSpan(0);
}
pub fn optionSpan(self: Arg, option_len: usize) Diagnostics.ErrorDetails.ArgSpan {
return .{
.name_offset = self.name_offset,
.prefix_len = self.prefixSlice().len,
.name_len = option_len,
};
}
pub fn looksLikeFilepath(self: Arg, io: Io) bool {
const meets_min_requirements = self.prefix == .slash and isSupportedInputExtension(std.fs.path.extension(self.full));
if (!meets_min_requirements) return false;
const could_be_fo_option = could_be_fo_option: {
var window_it = std.mem.window(u8, self.full[1..], 2, 1);
while (window_it.next()) |window| {
if (std.ascii.eqlIgnoreCase(window, "fo")) break :could_be_fo_option true;
// `/fo` option.
if (window[0] == '/') break;
}
break :could_be_fo_option false;
};
if (!could_be_fo_option) return true;
// be one, e.g. `/foo/bar.rc`. As a last ditch effort to reduce false negatives,
// check if the file path exists and, if so, then we ignore the 'could be /fo option'-ness
Io.Dir.accessAbsolute(io, self.full, .{}) catch return false;
return true;
}
pub const Value = struct {
slice: []const u8,
index_increment: u2 = 1,
pub fn argSpan(self: Value, arg: Arg) Diagnostics.ErrorDetails.ArgSpan {
const prefix_len = arg.prefixSlice().len;
switch (self.index_increment) {
1 => return .{
.value_offset = @intFromPtr(self.slice.ptr) - @intFromPtr(arg.full.ptr),
.prefix_len = prefix_len,
.name_offset = arg.name_offset,
},
2 => return .{
.point_at_next_arg = true,
.prefix_len = prefix_len,
.name_offset = arg.name_offset,
},
else => unreachable,
}
}
pub fn index(self: Value, arg_index: usize) usize {
if (self.index_increment == 2) return arg_index + 1;
return arg_index;
}
};
pub fn value(self: Arg, option_len: usize, index: usize, args: []const []const u8) error{MissingValue}!Value {
const rest = self.full[self.name_offset + option_len ..];
if (rest.len > 0) return .{ .slice = rest };
if (index + 1 >= args.len) return error.MissingValue;
return .{ .slice = args[index + 1], .index_increment = 2 };
}
pub const Context = struct {
index: usize,
option_len: usize,
arg: Arg,
value: Value,
};
}