Files
ghostty-rs/crates/ghostty-config/src/conditional.rs
T
ddidderr 33d15a2dc2 feat(config): port the configuration system and CLI argument parser
Ports ZIG/src/config (minus C/WASM APIs) and cli/{args,CommaSplitter,
diagnostics}.zig into ghostty-config:
- all 202 options with exact names, types, defaults and verbatim docs,
  declared through one config_struct! macro; Zig's comptime reflection is
  replaced by the ConfigField/CliTarget traits
- loading: default XDG files, -e, config-file includes (optional '?',
  cycle detection), themes incl. light:/dark: pairs, conditional config,
  replay, finalize() derived defaults, clone/changed diffing for reload
- Linux/GTK default keybinds built on ghostty_input::binding::Set
- formatter producing +show-config output

Verified against the installed Ghostty 1.3.1: `+show-config --default`
and `--default --docs` match byte for byte (except gtk-opengl-debug,
which differs between debug/release builds), as do two complex loaded
configs.

Open items (docs/rewrite/notes/ghostty-config.md): url regex needs an
Oniguruma-compatible engine (onig) in the renderer; some font/terminal
types are local copies awaiting integration; KeyRemapSet equality is
order sensitive unlike Zig.

Test Plan:
- cargo test -p ghostty-config (255 tests)
- compare `ghostty +show-config --default --docs` with our formatter
2026-10-11 22:00:12 +02:00

135 lines
3.1 KiB
Rust

//! Conditional configuration state (port of `config/conditional.zig`).
//!
//! Conditionals in Ghostty configuration are based on a static, typed
//! state of the world instead of a dynamic key-value set.
/// The theme of the underlying OS desktop environment.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
pub enum Theme {
#[default]
Light,
Dark,
}
impl Theme {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Theme::Light => "light",
Theme::Dark => "dark",
}
}
}
/// The target OS (Zig: `std.Target.Os.Tag`); only the Linux build is
/// supported, but the other tags are kept so matching by name works.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Os {
Linux,
Macos,
Windows,
Other,
}
impl Os {
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Os::Linux => "linux",
Os::Macos => "macos",
Os::Windows => "windows",
Os::Other => "other",
}
}
/// The OS of the current build.
#[must_use]
pub fn current() -> Os {
if cfg!(target_os = "linux") {
Os::Linux
} else if cfg!(target_os = "macos") {
Os::Macos
} else if cfg!(target_os = "windows") {
Os::Windows
} else {
Os::Other
}
}
}
/// The static, typed state conditionals are matched against.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct State {
/// The theme of the underlying OS desktop environment.
pub theme: Theme,
/// The target OS of the current build.
pub os: Os,
}
impl Default for State {
fn default() -> Self {
Self {
theme: Theme::Light,
os: Os::current(),
}
}
}
impl State {
/// Tests the conditional against the state and returns true if it matches.
#[must_use]
pub fn match_(&self, cond: &Conditional) -> bool {
let value = match cond.key {
Key::Theme => self.theme.as_str(),
Key::Os => self.os.as_str(),
};
match cond.op {
Op::Eq => value == cond.value,
Op::Ne => value != cond.value,
}
}
}
/// An enum of the available conditional configuration keys.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Key {
Theme,
Os,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Op {
Eq,
Ne,
}
/// A single conditional that can be true or false.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Conditional {
pub key: Key,
pub op: Op,
pub value: String,
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn conditional_enum_match() {
let state = State {
theme: Theme::Dark,
..State::default()
};
let c = |op, v: &str| Conditional {
key: Key::Theme,
op,
value: v.to_string(),
};
assert!(state.match_(&c(Op::Eq, "dark")));
assert!(!state.match_(&c(Op::Ne, "dark")));
assert!(state.match_(&c(Op::Ne, "light")));
}
}