use std::{
env, io,
path::{Path, PathBuf},
};
#[cfg(test)]
mod tests;
pub fn current_dir_to_file_parent_dir_relative_path(file_path: &str) -> io::Result<PathBuf> {
let parent = Path::new(file_path)
.parent()
.unwrap_or_else(|| Path::new(""));
let parent = if parent.as_os_str().is_empty() {
Path::new(".")
} else {
parent
};
relative_path(env::current_dir()?.as_path(), parent)
}
pub fn relative_path(from: &Path, to: &Path) -> io::Result<PathBuf> {
let from_abs = std::fs::canonicalize(from)?;
let to_abs = std::fs::canonicalize(to)?;
let mut from_iter = from_abs.components();
let mut to_iter = to_abs.components();
let mut from_rest = vec![];
let mut to_rest = vec![];
let mut common_prefix = vec![];
loop {
match (from_iter.next(), to_iter.next()) {
(Some(f), Some(t)) if f == t => {
common_prefix.push(f);
}
(Some(f), Some(t)) => {
from_rest.push(f);
to_rest.push(t);
from_rest.extend(from_iter);
to_rest.extend(to_iter);
break;
}
(Some(f), None) => {
from_rest.push(f);
from_rest.extend(from_iter);
break;
}
(None, Some(t)) => {
to_rest.push(t);
to_rest.extend(to_iter);
break;
}
(None, None) => break,
}
}
let mut result = PathBuf::new();
for _ in from_rest {
result.push("..");
}
for t in to_rest {
result.push(t.as_os_str());
}
if result.as_os_str().is_empty() {
Ok(PathBuf::from("."))
} else {
Ok(result)
}
}