mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00

Fixes #2791 This goes back to the previous implementation of clone that directly cloned values rather than using replay steps, because replay steps don't preserve conditionals on the iterator. This works. Another fix will be when we support some sort of conditional syntax and the replay iterator can yield that information. We have a unit test here so we can verify that then.
96 lines
2.8 KiB
Zig
96 lines
2.8 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const assert = std.debug.assert;
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
/// Conditionals in Ghostty configuration are based on a static, typed
|
|
/// state of the world instead of a dynamic key-value set. This simplifies
|
|
/// the implementation, allows for better type checking, and enables a
|
|
/// typed C API.
|
|
pub const State = struct {
|
|
/// The theme of the underlying OS desktop environment.
|
|
theme: Theme = .light,
|
|
|
|
/// The target OS of the current build.
|
|
os: std.Target.Os.Tag = builtin.target.os.tag,
|
|
|
|
pub const Theme = enum { light, dark };
|
|
|
|
/// Tests the conditional against the state and returns true if it matches.
|
|
pub fn match(self: State, cond: Conditional) bool {
|
|
switch (cond.key) {
|
|
inline else => |tag| {
|
|
// The raw value of the state field.
|
|
const raw = @field(self, @tagName(tag));
|
|
|
|
// Since all values are enums currently then we can just
|
|
// do this. If we introduce non-enum state values then this
|
|
// will be a compile error and we should fix here.
|
|
const value: []const u8 = @tagName(raw);
|
|
|
|
return switch (cond.op) {
|
|
.eq => std.mem.eql(u8, value, cond.value),
|
|
.ne => !std.mem.eql(u8, value, cond.value),
|
|
};
|
|
},
|
|
}
|
|
}
|
|
};
|
|
|
|
/// An enum of the available conditional configuration keys.
|
|
pub const Key = key: {
|
|
const stateInfo = @typeInfo(State).Struct;
|
|
var fields: [stateInfo.fields.len]std.builtin.Type.EnumField = undefined;
|
|
for (stateInfo.fields, 0..) |field, i| fields[i] = .{
|
|
.name = field.name,
|
|
.value = i,
|
|
};
|
|
|
|
break :key @Type(.{ .Enum = .{
|
|
.tag_type = std.math.IntFittingRange(0, fields.len - 1),
|
|
.fields = &fields,
|
|
.decls = &.{},
|
|
.is_exhaustive = true,
|
|
} });
|
|
};
|
|
|
|
/// A single conditional that can be true or false.
|
|
pub const Conditional = struct {
|
|
key: Key,
|
|
op: Op,
|
|
value: []const u8,
|
|
|
|
pub const Op = enum { eq, ne };
|
|
|
|
pub fn clone(
|
|
self: Conditional,
|
|
alloc: Allocator,
|
|
) Allocator.Error!Conditional {
|
|
return .{
|
|
.key = self.key,
|
|
.op = self.op,
|
|
.value = try alloc.dupe(u8, self.value),
|
|
};
|
|
}
|
|
};
|
|
|
|
test "conditional enum match" {
|
|
const testing = std.testing;
|
|
const state: State = .{ .theme = .dark };
|
|
try testing.expect(state.match(.{
|
|
.key = .theme,
|
|
.op = .eq,
|
|
.value = "dark",
|
|
}));
|
|
try testing.expect(!state.match(.{
|
|
.key = .theme,
|
|
.op = .ne,
|
|
.value = "dark",
|
|
}));
|
|
try testing.expect(state.match(.{
|
|
.key = .theme,
|
|
.op = .ne,
|
|
.value = "light",
|
|
}));
|
|
}
|