#![allow(dead_code)]
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use tempfile::TempDir;
const XDG_VARS: &[&str] = &[
"HOME",
"XDG_CONFIG_HOME",
"XDG_DATA_HOME",
"XDG_STATE_HOME",
"XDG_CACHE_HOME",
];
const SCRUBBED_VARS: &[&str] = &["QLI_ENV"];
pub struct XdgSandbox {
tmp: TempDir,
saved: Vec<(&'static str, Option<OsString>)>,
}
impl XdgSandbox {
pub fn new() -> Self {
let tmp = tempfile::tempdir().expect("create xdg sandbox tempdir");
let mut sandbox = Self {
tmp,
saved: Vec::with_capacity(XDG_VARS.len() + SCRUBBED_VARS.len()),
};
for &var in XDG_VARS {
sandbox.saved.push((var, std::env::var_os(var)));
let dir = sandbox.tmp.path().join(subdir_for(var));
std::fs::create_dir_all(&dir).expect("create xdg subdir");
std::env::set_var(var, &dir);
}
for &var in SCRUBBED_VARS {
sandbox.saved.push((var, std::env::var_os(var)));
std::env::remove_var(var);
}
sandbox
}
pub fn path(&self) -> &Path {
self.tmp.path()
}
pub fn extensions_dir(&self) -> PathBuf {
self.tmp.path().join("data").join("qli").join("extensions")
}
pub fn state_dir(&self) -> PathBuf {
self.tmp.path().join("state").join("qli")
}
}
impl Drop for XdgSandbox {
fn drop(&mut self) {
for (var, prev) in self.saved.drain(..) {
match prev {
Some(v) => std::env::set_var(var, v),
None => std::env::remove_var(var),
}
}
}
}
fn subdir_for(var: &str) -> &'static str {
match var {
"HOME" => "home",
"XDG_CONFIG_HOME" => "config",
"XDG_DATA_HOME" => "data",
"XDG_STATE_HOME" => "state",
"XDG_CACHE_HOME" => "cache",
_ => unreachable!("unknown xdg var: {var}"),
}
}
#[cfg(unix)]
pub fn stage_extension(
sandbox: &XdgSandbox,
group: &str,
name: &str,
manifest_toml: &str,
script_body: &str,
) {
use std::os::unix::fs::PermissionsExt;
let group_dir = sandbox.extensions_dir().join(group);
std::fs::create_dir_all(&group_dir).expect("create group dir");
std::fs::write(group_dir.join("_manifest.toml"), manifest_toml).expect("write manifest");
let script = group_dir.join(name);
std::fs::write(&script, script_body).expect("write script");
let mut perms = std::fs::metadata(&script).unwrap().permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&script, perms).expect("chmod script");
}