fix: last RV11 gaps - bench enum args, theme NUL paths, Zig error names

Resolves the 3 remaining gaps in docs/rewrite/notes/review-rv11-codex.md
(final-Z14.md):
- ghostty-bench validates every occurrence of enum options in order
  (Args::take_enum; empty value resets) like cli/args.zig, so
  `--mode=bad --mode=noop` fails as in Zig.
- Theme paths with an interior NUL open the prefix like Zig's C-string
  path handling (verified against /usr/bin/ghostty).
- Filesystem diagnostics print Zig's error names (error.FileNotFound,
  NotDir, AccessDenied, IsDir, NameTooLong, SymLinkLoop, ...) via a
  shared ghostty_os::zig_error mapping used by theme/path/config-file/
  file_load/edit-config/list-themes; messages match the oracle.

Test Plan:
- cargo test --workspace --no-fail-fast
- just check-macos (both), just check-windows, just check-wasm
This commit is contained in:
ddidderr committed 2026-10-12 13:54:15 +02:00
1 parent ce9373aa7a
commit f9b333545a
12 files changed
+215 -89

No files matched your search

+2 -36
View File
@@ -3752,7 +3752,7 @@ crate::config_struct! {
/// Errors from loading configuration.
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{0}")]
#[error("{}", ghostty_os::zig_error::io_error_name(.0))]
Io(#[from] std::io::Error),
#[error("{0}")]
Arg(#[from] ArgError),
@@ -4273,7 +4273,7 @@ impl Config {
message: format!(
"error opening config-file {}: {}",
path.as_encoded(),
zig_io_error_name(&err)
ghostty_os::zig_error::io_error_name(&err)
),
..Diagnostic::default()
};
@@ -4806,40 +4806,6 @@ impl Iterator for ChangeIterator<'_> {
}
}
/// Name of an IO error like Zig's `{}` formatting of an error value
/// (`error.FileNotFound`), used in user-visible diagnostics.
fn zig_io_error_name(err: &std::io::Error) -> String {
use std::io::ErrorKind;
#[cfg(unix)]
if let Some(errno) = err.raw_os_error() {
let name = match errno {
2 => Some("FileNotFound"),
13 => Some("AccessDenied"),
1 => Some("PermissionDenied"),
21 => Some("IsDir"),
20 => Some("NotDir"),
40 => Some("SymLinkLoop"),
36 => Some("NameTooLong"),
24 => Some("ProcessFdQuotaExceeded"),
23 => Some("SystemFdQuotaExceeded"),
12 => Some("SystemResources"),
19 => Some("NoDevice"),
16 => Some("DeviceBusy"),
26 => Some("FileBusy"),
30 => Some("ReadOnlyFileSystem"),
_ => None,
};
if let Some(name) = name {
return format!("error.{name}");
}
}
match err.kind() {
ErrorKind::NotFound => "error.FileNotFound".to_string(),
ErrorKind::PermissionDenied => "error.AccessDenied".to_string(),
_ => "error.Unexpected".to_string(),
}
}
fn file_kind_name(meta: &std::fs::Metadata) -> &'static str {
let ft = meta.file_type();
if ft.is_dir() {