Skip to main content

ytil_sys/
dir.rs

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