Skip to main content

ytil_sys/
dir.rs

1use std::path::Path;
2use std::path::PathBuf;
3
4use rootcause::option_ext::OptionExt as _;
5use rootcause::prelude::ResultExt as _;
6
7/// Builds a path starting from the home directory by appending the given parts, returning a [`PathBuf`].
8///
9/// # Errors
10/// - The home directory cannot be determined.
11pub fn build_home_path<P: AsRef<Path>>(parts: &[P]) -> rootcause::Result<PathBuf> {
12    let home_path = home::home_dir().context("missing home dir").attach("env=HOME")?;
13    Ok(build_path(home_path, parts))
14}
15
16/// Builds a path by appending multiple parts to a root path.
17pub fn build_path<P: AsRef<Path>>(mut root: PathBuf, parts: &[P]) -> PathBuf {
18    for part in parts {
19        root.push(part);
20    }
21    root
22}
23
24/// Resolve workspace root directory.
25///
26/// Ascends three levels from this crate's manifest.
27///
28/// # Errors
29/// - Directory traversal fails (unexpected layout).
30pub fn get_workspace_root() -> rootcause::Result<PathBuf> {
31    let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
32    Ok(manifest_dir
33        .parent()
34        .and_then(|p| p.parent())
35        .and_then(|p| p.parent())
36        .context(format!(
37            "cannot get workspace root | manifest_dir={}",
38            manifest_dir.display()
39        ))?
40        .to_path_buf())
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn build_path_appends_parts_to_root() {
49        let root = PathBuf::from("/base");
50        let result = build_path(root, &["a", "b", "c"]);
51        pretty_assertions::assert_eq!(result, PathBuf::from("/base/a/b/c"));
52    }
53
54    #[test]
55    fn build_path_with_empty_parts_returns_root() {
56        let root = PathBuf::from("/base");
57        let result = build_path(root, &[] as &[&str]);
58        pretty_assertions::assert_eq!(result, PathBuf::from("/base"));
59    }
60
61    #[test]
62    fn build_home_path_returns_path_ending_with_parts() {
63        assert2::assert!(let Ok(path) = build_home_path(&[".config", "test"]));
64        assert!(path.ends_with(".config/test"), "path={}", path.display());
65    }
66
67    #[test]
68    fn get_workspace_root_returns_existing_directory() {
69        assert2::assert!(let Ok(root) = get_workspace_root());
70        assert!(root.is_dir(), "root={}", root.display());
71        // The workspace root should contain Cargo.toml
72        assert!(root.join("Cargo.toml").exists(), "root={}", root.display());
73    }
74}