use std::path::{Path, PathBuf};
use crate::cli_error::CliError;
use crate::output::json::JsonError;
pub(crate) fn absolutize_for_json(path: &Path) -> Result<PathBuf, anyhow::Error> {
std::path::absolute(path).map_err(|source| {
CliError::runtime(JsonError {
code: "path::resolution_failed".into(),
message: format!("could not resolve path {path:?}: {source}"),
field: None,
details: None,
diagnostics: vec![],
cause: vec![],
})
})
}
pub(crate) fn paths_overlap(
index_path: &Path,
out_dir: &Path,
index_display: &str,
out_display: &str,
) -> anyhow::Result<bool> {
let idx = std::fs::canonicalize(index_path).map_err(|e| {
CliError::runtime(JsonError {
code: "io::canonicalize_failed".into(),
message: format!("failed to canonicalize --index {index_display}: {e}"),
field: Some(index_display.to_owned()),
details: Some(serde_json::json!({
"path": index_display,
"io_kind": format!("{:?}", e.kind()),
})),
diagnostics: vec![],
cause: vec![e.to_string()],
})
})?;
let out = std::fs::canonicalize(out_dir).map_err(|e| {
CliError::runtime(JsonError {
code: "io::canonicalize_failed".into(),
message: format!("failed to canonicalize --out {out_display}: {e}"),
field: Some(out_display.to_owned()),
details: Some(serde_json::json!({
"path": out_display,
"io_kind": format!("{:?}", e.kind()),
})),
diagnostics: vec![],
cause: vec![e.to_string()],
})
})?;
let Some(idx_parent) = idx.parent() else {
return Ok(false);
};
Ok(idx_parent == out)
}
pub(crate) fn display_relative_to_cwd(path: &Path) -> String {
let Ok(cwd) = std::env::current_dir() else {
return path.display().to_string();
};
display_relative_to(path, &cwd)
}
fn display_relative_to(path: &Path, cwd: &Path) -> String {
match path.strip_prefix(cwd) {
Ok(rel) if rel.as_os_str().is_empty() => ".".to_string(),
Ok(rel) => rel.display().to_string(),
Err(_) => path.display().to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
#[test]
fn descendant_path_is_shortened() {
let cwd = PathBuf::from("/home/u/proj");
let nested = cwd.join("build").join("foo.tar.gz");
assert_eq!(
display_relative_to(&nested, &cwd),
PathBuf::from("build")
.join("foo.tar.gz")
.display()
.to_string()
);
}
#[test]
fn cwd_itself_renders_as_dot() {
let cwd = PathBuf::from("/home/u/proj");
assert_eq!(display_relative_to(&cwd, &cwd), ".");
}
#[test]
fn non_descendant_path_falls_back_to_absolute() {
let cwd = PathBuf::from("/home/u/proj");
let other = PathBuf::from("/var/lib/something/file");
assert_eq!(
display_relative_to(&other, &cwd),
other.display().to_string()
);
}
#[test]
fn sibling_path_falls_back_to_absolute() {
let cwd = PathBuf::from("/home/u/proj");
let sibling = PathBuf::from("/home/u/other/file");
assert_eq!(
display_relative_to(&sibling, &cwd),
sibling.display().to_string()
);
}
}