use std::path::{Path, PathBuf};
use crate::utils::collapse_project_path;
use super::git_ops::git_repository_root;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ServiceScopeFilter {
Versioning,
Release,
}
pub(crate) const LAST_VERSION_SCOPE_FILE: &str = "last-version-scope";
pub(crate) fn normalized_relative_path(path: &Path) -> String {
path.to_string_lossy().replace('\\', "/")
}
pub(crate) fn ledger_relative_path(project_root: &Path, path: &Path) -> String {
let collapsed = collapse_project_path(project_root, &path.to_string_lossy());
let relative = if collapsed.is_empty() || collapsed == "./" {
path.strip_prefix(project_root)
.map(|rel| rel.to_string_lossy().into_owned())
.unwrap_or_else(|_| path.to_string_lossy().into_owned())
} else {
collapsed
};
relative.replace('\\', "/")
}
pub(crate) fn ledger_relative_paths(project_root: &Path, paths: &[PathBuf]) -> Vec<String> {
paths
.iter()
.map(|path| ledger_relative_path(project_root, path))
.collect()
}
pub(crate) fn same_filesystem_path(left: &Path, right: &Path) -> bool {
if left == right {
return true;
}
match (left.canonicalize(), right.canonicalize()) {
(Ok(left), Ok(right)) => left == right,
_ => false,
}
}
pub(crate) fn enclosing_git_root(dir: &Path) -> Option<PathBuf> {
for ancestor in dir.ancestors() {
if ancestor.join(".git").exists() {
return Some(ancestor.to_path_buf());
}
}
git_repository_root(dir)
}
#[cfg(test)]
mod tests {
use super::ledger_relative_path;
use std::path::PathBuf;
#[test]
fn ledger_paths_are_repo_relative_with_forward_slashes() {
let root = PathBuf::from(r"C:\Users\floris\Documents\GitHub\data-models");
let absolute = root.join("CHANGELOG.md");
let relative = ledger_relative_path(&root, &absolute);
assert_eq!(relative, "CHANGELOG.md");
let nested = root.join(".xbp").join("releases").join("x.toml");
assert_eq!(
ledger_relative_path(&root, &nested),
".xbp/releases/x.toml"
);
}
}