use std::io::Write;
use std::path::{Path, PathBuf};
static BUNDLE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/zsh_functions.zst"));
const STAMP: &str = ".zshrs-bundle-version";
const INSTALL_LOCK: &str = ".zshrs-bundle-install.lock";
include!(concat!(env!("OUT_DIR"), "/zsh_functions_id.rs"));
fn stamp_value() -> String {
BUNDLE_ID.to_string()
}
pub fn functions_dir() -> Option<PathBuf> {
let home = std::env::var_os("HOME")?;
Some(PathBuf::from(home).join(".zshrs").join("functions"))
}
pub fn is_bundle_dir(dir: &Path) -> bool {
dir.join(STAMP).is_file()
}
fn needs_write(dir: &Path) -> bool {
match std::fs::read_to_string(dir.join(STAMP)) {
Ok(s) => s.trim() != stamp_value(),
Err(_) => true,
}
}
fn write_atomically(dir: &Path, name: &str, body: &[u8]) -> bool {
let tmp = dir.join(format!(".{name}.{}.tmp", std::process::id()));
let dest = dir.join(name);
if std::fs::read(&dest).is_ok_and(|have| have == body) {
return true;
}
let ok = std::fs::File::create(&tmp)
.and_then(|mut f| f.write_all(body))
.and_then(|()| std::fs::rename(&tmp, &dest))
.is_ok();
if !ok {
let _ = std::fs::remove_file(&tmp);
}
ok
}
pub fn ensure_installed() -> Option<usize> {
let dir = functions_dir()?;
if !needs_write(&dir) {
return Some(0);
}
std::fs::create_dir_all(&dir).ok()?;
let _installing = std::fs::File::options()
.create(true)
.truncate(false)
.write(true)
.open(dir.join(INSTALL_LOCK))
.ok()
.and_then(|f| nix::fcntl::Flock::lock(f, nix::fcntl::FlockArg::LockExclusive).ok());
if !needs_write(&dir) {
return Some(0);
}
let raw = zstd::decode_all(BUNDLE).ok()?;
let mut n = 0usize;
let mut i = 0usize;
while i + 4 <= raw.len() {
let nl = u32::from_le_bytes(raw[i..i + 4].try_into().ok()?) as usize;
i += 4;
if i + nl + 4 > raw.len() {
break;
}
let name = String::from_utf8_lossy(&raw[i..i + nl]).into_owned();
i += nl;
let bl = u32::from_le_bytes(raw[i..i + 4].try_into().ok()?) as usize;
i += 4;
if i + bl > raw.len() {
break;
}
let body = &raw[i..i + bl];
i += bl;
if name.contains('/') || name.contains("..") || name.is_empty() {
continue;
}
if write_atomically(&dir, &name, body) {
n += 1;
}
}
write_atomically(&dir, STAMP, stamp_value().as_bytes());
tracing::info!(target: "bundled_functions", written = n, dir = %dir.display(),
"materialised bundled zsh functions");
Some(n)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_reader_never_sees_a_partly_written_file() {
let dir = tempfile::tempdir().expect("tempdir");
let (a, b) = (vec![b'a'; 32 * 1024], vec![b'b'; 32 * 1024]);
assert!(write_atomically(dir.path(), "_probe", &a));
let path = dir.path().join("_probe");
let stop = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let reader = {
let (path, stop, len) = (path.clone(), stop.clone(), a.len());
std::thread::spawn(move || {
let mut torn = 0usize;
while !stop.load(std::sync::atomic::Ordering::Relaxed) {
if let Ok(read) = std::fs::read(&path) {
let whole = read.len() == len
&& (read.iter().all(|&c| c == b'a') || read.iter().all(|&c| c == b'b'));
if !whole {
torn += 1;
}
}
}
torn
})
};
for round in 0..200 {
assert!(write_atomically(dir.path(), "_probe", if round % 2 == 0 { &b } else { &a }));
}
stop.store(true, std::sync::atomic::Ordering::Relaxed);
assert_eq!(reader.join().expect("reader"), 0, "a read saw a truncated or partial file");
}
#[test]
fn an_unchanged_file_is_left_alone() {
let dir = tempfile::tempdir().expect("tempdir");
assert!(write_atomically(dir.path(), "_same", b"#compdef same\n"));
let first = std::fs::metadata(dir.path().join("_same")).unwrap().modified().unwrap();
std::thread::sleep(std::time::Duration::from_millis(20));
assert!(write_atomically(dir.path(), "_same", b"#compdef same\n"));
let second = std::fs::metadata(dir.path().join("_same")).unwrap().modified().unwrap();
assert_eq!(first, second, "identical content must not be rewritten");
assert!(write_atomically(dir.path(), "_same", b"#compdef other\n"));
assert_eq!(std::fs::read(dir.path().join("_same")).unwrap(), b"#compdef other\n");
}
#[test]
fn the_temporary_is_gone_after_the_rename() {
let dir = tempfile::tempdir().expect("tempdir");
assert!(write_atomically(dir.path(), "_one", b"#compdef one\n"));
let names: Vec<String> = std::fs::read_dir(dir.path())
.expect("read_dir")
.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().into_owned())
.collect();
assert_eq!(names, vec!["_one".to_string()]);
}
}