1use eyre::{Context as _, Result, eyre};
4use simply_colored::*;
5use std::{
6 iter,
7 path::{Path, PathBuf},
8};
9
10#[easy_ext::ext(PathExt)]
12pub impl<T: AsRef<Path>> T {
13 #[allow(clippy::disallowed_methods, reason = "definition of `show_path`")]
15 fn show(&self) -> String {
16 format!("{CYAN}{}{RESET}", self.as_ref().display())
17 }
18
19 fn strip_prefix(&self, prefix: impl AsRef<Path>) -> Result<&Path> {
21 self.as_ref().strip_prefix(&prefix).with_context(|| {
22 eyre!(
23 "failed to strip prefix {} from {}",
24 prefix.show(),
25 self.show()
26 )
27 })
28 }
29}
30
31pub fn traverse_upwards(base_dir: impl AsRef<Path>) -> impl Iterator<Item = PathBuf> {
40 let mut current_dir = Some(base_dir.as_ref().to_path_buf());
41 iter::once(base_dir.as_ref().to_path_buf()).chain(iter::from_fn(move || {
42 if let Some(d) = ¤t_dir {
43 current_dir = d.parent().map(Path::to_path_buf);
44 current_dir.clone()
45 } else {
46 None
47 }
48 }))
49}
50
51#[cfg(test)]
52mod tests {
53 use itertools::Itertools as _;
54
55 use super::*;
56
57 #[test]
58 fn traverse_upwards() {
59 let path = PathBuf::from("/home/user/project/name/");
60
61 assert_eq!(
62 super::traverse_upwards(path).collect_vec(),
63 vec![
64 PathBuf::from("/home/user/project/name/"),
65 PathBuf::from("/home/user/project/"),
66 PathBuf::from("/home/user/"),
67 PathBuf::from("/home/"),
68 PathBuf::from("/"),
69 ]
70 );
71 }
72}