1use std::path::Path;
2use std::path::PathBuf;
3
4use rootcause::option_ext::OptionExt as _;
5#[cfg(not(target_arch = "wasm32"))]
6use rootcause::prelude::ResultExt as _;
7
8#[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
18pub 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
26pub 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 super::*;
48
49 #[test]
50 fn test_build_path_appends_parts_to_root() {
51 let root = PathBuf::from("/base");
52 let result = build_path(root, &["a", "b", "c"]);
53 pretty_assertions::assert_eq!(result, PathBuf::from("/base/a/b/c"));
54 }
55
56 #[test]
57 fn test_build_path_with_empty_parts_returns_root() {
58 let root = PathBuf::from("/base");
59 let result = build_path(root, &[] as &[&str]);
60 pretty_assertions::assert_eq!(result, PathBuf::from("/base"));
61 }
62
63 #[test]
64 #[cfg(not(target_arch = "wasm32"))]
65 fn test_build_home_path_returns_path_ending_with_parts() {
66 assert2::assert!(let Ok(path) = build_home_path(&[".config", "test"]));
67 assert!(path.ends_with(".config/test"), "path={}", path.display());
68 }
69
70 #[test]
71 fn test_get_workspace_root_returns_existing_directory() {
72 assert2::assert!(let Ok(root) = get_workspace_root());
73 assert!(root.is_dir(), "root={}", root.display());
74 assert!(root.join("Cargo.toml").exists(), "root={}", root.display());
76 }
77}