1use std::path::PathBuf;
2
3#[derive(Debug, thiserror::Error)]
4pub enum PathError {
5 #[error("Could not execute Cargo to find the project root directory.")]
6 ExecutingCargo(std::io::Error),
7 #[error("Cargo could not locate the project root directory.")]
8 LocatingWorkspaceRoot,
9 #[error("Path to the project root directory is not valid UTF8.")]
10 InvalidPath,
11 #[error("Could not get the current directory.")]
12 CurrentDir,
13}
14
15pub fn get_cargo_root() -> Result<PathBuf, PathError> {
18 let locate_project_output = std::process::Command::new(env!("CARGO"))
19 .arg("locate-project")
20 .arg("--workspace")
21 .arg("--quiet")
22 .arg("--message-format=plain")
23 .current_dir(
24 std::env::var("CARGO_MANIFEST_DIR")
25 .map(PathBuf::from)
26 .or(std::env::current_dir())
27 .map_err(|_| PathError::CurrentDir)?,
28 )
29 .output()
30 .map_err(PathError::ExecutingCargo)?;
31
32 if locate_project_output.status.success() {
33 let workspace_root = PathBuf::from(
34 String::from_utf8(locate_project_output.stdout).map_err(|_| PathError::InvalidPath)?,
35 );
36 Ok(workspace_root
37 .parent()
38 .map(|p| p.to_path_buf())
39 .unwrap_or_default())
40 } else {
41 Err(PathError::LocatingWorkspaceRoot)
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use std::path::PathBuf;
48
49 use super::get_cargo_root;
50
51 #[test]
52 fn workspace_root_of_mantra() {
53 let workspace_root = get_cargo_root().unwrap().canonicalize().unwrap();
54 let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
55 let expected_root = PathBuf::from(manifest_dir).canonicalize().unwrap();
56
57 assert_eq!(
58 workspace_root, expected_root,
59 "Returned workspace root is wrong."
60 );
61 }
62}