mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-15 16:26:08 +03:00
cli: positional arguments are invalid when parsing configuration
This commit is contained in:
109
src/cli/args.zig
109
src/cli/args.zig
@ -93,46 +93,60 @@ pub fn parse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mem.startsWith(u8, arg, "--")) {
|
// If this doesn't start with "--" then it isn't a config
|
||||||
var key: []const u8 = arg[2..];
|
// flag. We don't support positional arguments or configuration
|
||||||
const value: ?[]const u8 = value: {
|
// values set with spaces so this is an error.
|
||||||
// If the arg has "=" then the value is after the "=".
|
if (!mem.startsWith(u8, arg, "--")) {
|
||||||
if (mem.indexOf(u8, key, "=")) |idx| {
|
if (comptime !canTrackDiags(T)) return Error.InvalidField;
|
||||||
defer key = key[0..idx];
|
|
||||||
break :value key[idx + 1 ..];
|
|
||||||
}
|
|
||||||
|
|
||||||
break :value null;
|
// Add our diagnostic
|
||||||
};
|
try dst._diagnostics.append(arena_alloc, .{
|
||||||
|
.key = try arena_alloc.dupeZ(u8, arg),
|
||||||
|
.message = "invalid field",
|
||||||
|
.location = diags.Location.fromIter(iter),
|
||||||
|
});
|
||||||
|
|
||||||
parseIntoField(T, arena_alloc, dst, key, value) catch |err| {
|
continue;
|
||||||
if (comptime !canTrackDiags(T)) return err;
|
|
||||||
|
|
||||||
// The error set is dependent on comptime T, so we always add
|
|
||||||
// an extra error so we can have the "else" below.
|
|
||||||
const ErrSet = @TypeOf(err) || error{Unknown};
|
|
||||||
const message: [:0]const u8 = switch (@as(ErrSet, @errorCast(err))) {
|
|
||||||
// OOM is not recoverable since we need to allocate to
|
|
||||||
// track more error messages.
|
|
||||||
error.OutOfMemory => return err,
|
|
||||||
error.InvalidField => "unknown field",
|
|
||||||
error.ValueRequired => "value required",
|
|
||||||
error.InvalidValue => "invalid value",
|
|
||||||
else => try std.fmt.allocPrintZ(
|
|
||||||
arena_alloc,
|
|
||||||
"unknown error {}",
|
|
||||||
.{err},
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add our diagnostic
|
|
||||||
try dst._diagnostics.append(arena_alloc, .{
|
|
||||||
.key = try arena_alloc.dupeZ(u8, key),
|
|
||||||
.message = message,
|
|
||||||
.location = diags.Location.fromIter(iter),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var key: []const u8 = arg[2..];
|
||||||
|
const value: ?[]const u8 = value: {
|
||||||
|
// If the arg has "=" then the value is after the "=".
|
||||||
|
if (mem.indexOf(u8, key, "=")) |idx| {
|
||||||
|
defer key = key[0..idx];
|
||||||
|
break :value key[idx + 1 ..];
|
||||||
|
}
|
||||||
|
|
||||||
|
break :value null;
|
||||||
|
};
|
||||||
|
|
||||||
|
parseIntoField(T, arena_alloc, dst, key, value) catch |err| {
|
||||||
|
if (comptime !canTrackDiags(T)) return err;
|
||||||
|
|
||||||
|
// The error set is dependent on comptime T, so we always add
|
||||||
|
// an extra error so we can have the "else" below.
|
||||||
|
const ErrSet = @TypeOf(err) || error{Unknown};
|
||||||
|
const message: [:0]const u8 = switch (@as(ErrSet, @errorCast(err))) {
|
||||||
|
// OOM is not recoverable since we need to allocate to
|
||||||
|
// track more error messages.
|
||||||
|
error.OutOfMemory => return err,
|
||||||
|
error.InvalidField => "unknown field",
|
||||||
|
error.ValueRequired => "value required",
|
||||||
|
error.InvalidValue => "invalid value",
|
||||||
|
else => try std.fmt.allocPrintZ(
|
||||||
|
arena_alloc,
|
||||||
|
"unknown error {}",
|
||||||
|
.{err},
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add our diagnostic
|
||||||
|
try dst._diagnostics.append(arena_alloc, .{
|
||||||
|
.key = try arena_alloc.dupeZ(u8, key),
|
||||||
|
.message = message,
|
||||||
|
.location = diags.Location.fromIter(iter),
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,6 +466,27 @@ test "parse: empty value resets to default" {
|
|||||||
try testing.expect(!data.b);
|
try testing.expect(!data.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "parse: positional arguments are invalid" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
var data: struct {
|
||||||
|
a: u8 = 42,
|
||||||
|
_arena: ?ArenaAllocator = null,
|
||||||
|
} = .{};
|
||||||
|
defer if (data._arena) |arena| arena.deinit();
|
||||||
|
|
||||||
|
var iter = try std.process.ArgIteratorGeneral(.{}).init(
|
||||||
|
testing.allocator,
|
||||||
|
"--a=84 what",
|
||||||
|
);
|
||||||
|
defer iter.deinit();
|
||||||
|
try testing.expectError(
|
||||||
|
error.InvalidField,
|
||||||
|
parse(@TypeOf(data), testing.allocator, &data, &iter),
|
||||||
|
);
|
||||||
|
try testing.expectEqual(@as(u8, 84), data.a);
|
||||||
|
}
|
||||||
|
|
||||||
test "parse: diagnostic tracking" {
|
test "parse: diagnostic tracking" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
|
||||||
|
@ -46,6 +46,8 @@ pub const Location = union(enum) {
|
|||||||
line: usize,
|
line: usize,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
pub const Key = @typeInfo(Location).Union.tag_type.?;
|
||||||
|
|
||||||
pub fn fromIter(iter: anytype) Location {
|
pub fn fromIter(iter: anytype) Location {
|
||||||
const Iter = t: {
|
const Iter = t: {
|
||||||
const T = @TypeOf(iter);
|
const T = @TypeOf(iter);
|
||||||
@ -121,4 +123,17 @@ pub const DiagnosticList = struct {
|
|||||||
pub fn items(self: *const DiagnosticList) []const Diagnostic {
|
pub fn items(self: *const DiagnosticList) []const Diagnostic {
|
||||||
return self.list.items;
|
return self.list.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if there are any diagnostics for the given
|
||||||
|
/// location type.
|
||||||
|
pub fn containsLocation(
|
||||||
|
self: *const DiagnosticList,
|
||||||
|
location: Location.Key,
|
||||||
|
) bool {
|
||||||
|
for (self.list.items) |diag| {
|
||||||
|
if (diag.location == location) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user