use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PathStatus {
pub path: PathBuf,
pub exists: bool,
pub writable: bool,
pub changed: bool,
}
pub fn status(workspace_root: &Path, forbidden: &[String]) -> Vec<PathStatus> {
forbidden
.iter()
.map(|rel| {
let p = workspace_root.join(rel);
let (exists, writable) = inspect(&p);
PathStatus { path: p, exists, writable, changed: false }
})
.collect()
}
pub fn apply(workspace_root: &Path, forbidden: &[String]) -> Result<Vec<PathStatus>> {
chmod_each(workspace_root, forbidden, false)
}
pub fn release(workspace_root: &Path, forbidden: &[String]) -> Result<Vec<PathStatus>> {
chmod_each(workspace_root, forbidden, true)
}
fn inspect(p: &Path) -> (bool, bool) {
match std::fs::metadata(p) {
Ok(m) => (true, !m.permissions().readonly()),
Err(_) => (false, false),
}
}
#[cfg(unix)]
fn chmod_each(
workspace_root: &Path,
forbidden: &[String],
writable: bool,
) -> Result<Vec<PathStatus>> {
use std::os::unix::fs::PermissionsExt;
let mut out = Vec::new();
for rel in forbidden {
let p = workspace_root.join(rel);
if !p.exists() {
out.push(PathStatus { path: p, exists: false, writable: false, changed: false });
continue;
}
let meta = std::fs::metadata(&p)
.with_context(|| format!("stat {}", p.display()))?;
let before = !meta.permissions().readonly();
let mut perms = meta.permissions();
let mode = perms.mode();
let new_mode = if writable {
mode | 0o200 } else {
mode & !0o222 };
perms.set_mode(new_mode);
std::fs::set_permissions(&p, perms)
.with_context(|| format!("chmod {}", p.display()))?;
let (_, after) = inspect(&p);
out.push(PathStatus {
path: p,
exists: true,
writable: after,
changed: before != after,
});
}
Ok(out)
}
#[cfg(not(unix))]
fn chmod_each(
workspace_root: &Path,
forbidden: &[String],
writable: bool,
) -> Result<Vec<PathStatus>> {
let mut out = Vec::new();
for rel in forbidden {
let p = workspace_root.join(rel);
if !p.exists() {
out.push(PathStatus { path: p, exists: false, writable: false, changed: false });
continue;
}
let meta = std::fs::metadata(&p)?;
let before = !meta.permissions().readonly();
let mut perms = meta.permissions();
perms.set_readonly(!writable);
std::fs::set_permissions(&p, perms)?;
let (_, after) = inspect(&p);
out.push(PathStatus {
path: p,
exists: true,
writable: after,
changed: before != after,
});
}
Ok(out)
}