use std::fs;
use std::path::{Path, PathBuf};
use openmoat_hosts::{Host, PluginDir};
#[must_use]
pub fn replaced(cwd: &Path, user_home: &Path, host: Host, hook_file: &Path) -> Option<String> {
let dir = host.spec().project_settings.as_ref()?.plugin_dir.as_ref()?;
if !crate::project::trusted(cwd, user_home) {
return None;
}
let found = replacing(cwd, dir, hook_file)?;
Some(format!(
"{} replaces OpenMoat's plugin for a {} session started in this directory, which runs \
every call without OpenMoat; remove that directory",
found.display(),
host.display_name()
))
}
pub(super) fn replacing(root: &Path, dir: &PluginDir, hook_file: &Path) -> Option<PathBuf> {
let plugin = root.join(dir.path).join(dir.own);
fs::symlink_metadata(&plugin).ok()?;
let theirs = plugin.join(dir.hook_file);
let same_file = matches!(
(fs::canonicalize(&theirs), fs::canonicalize(hook_file)),
(Ok(a), Ok(b)) if a == b
);
let same_bytes = matches!(
(super::read_bytes(&theirs), super::read_bytes(hook_file)),
(Ok(Some(a)), Ok(Some(b))) if a == b
);
(!same_file && !same_bytes).then_some(plugin)
}
pub(super) fn others(root: &Path, dir: &PluginDir) -> Vec<PathBuf> {
let Ok(entries) = fs::read_dir(root.join(dir.path)) else {
return Vec::new();
};
let mut files: Vec<PathBuf> = entries
.filter_map(|entry| Some(entry.ok()?.path()))
.filter(|path| path.file_name().is_some_and(|name| name != dir.own))
.map(|path| path.join(dir.hook_file))
.filter(|file| file.is_file())
.collect();
files.sort();
files
}
#[must_use]
pub fn replaced_here(host: Host, hook_file: &Path) -> Option<String> {
let cwd = std::env::current_dir().ok()?;
let user_home = crate::home::user_home().ok()?;
replaced(&cwd, &user_home, host, hook_file)
}