project_root/
lib.rs

1//! # project root
2//!
3//! Helper to find the absolute root directory path of a project as it stands relative
4//! to the location of the nearest Cargo.lock file.
5
6use std::ffi::OsString;
7use std::fs::read_dir;
8use std::path::PathBuf;
9use std::{env, io};
10use std::io::ErrorKind;
11
12/// Get the project root (relative to closest Cargo.lock file)
13/// ```rust
14/// match project_root::get_project_root() {
15///     Ok(p) => println!("Current project root is {:?}", p),
16///     Err(e) => println!("Error obtaining project root {:?}", e)
17/// };
18/// ```
19pub fn get_project_root() -> io::Result<PathBuf> {
20    let path = env::current_dir()?;
21    let mut path_ancestors = path.as_path().ancestors();
22
23    while let Some(p) = path_ancestors.next() {
24        let has_cargo =
25            read_dir(p)?
26                .into_iter()
27                .any(|p| p.unwrap().file_name() == OsString::from("Cargo.lock"));
28        if has_cargo {
29            return Ok(PathBuf::from(p))
30        }
31    }
32    Err(io::Error::new(ErrorKind::NotFound, "Ran out of places to find Cargo.toml"))
33
34}
35
36#[cfg(test)]
37mod tests {
38    use crate::get_project_root;
39    use std::fs::read_to_string;
40
41    #[test]
42    fn it_should_find_our_project_root() {
43        let crate_name = "name = \"project-root\"";
44
45        let project_root = get_project_root().expect("There is no project root");
46
47        let toml_path = project_root.to_str().unwrap().to_owned() + "/Cargo.toml";
48        let toml_string = read_to_string(toml_path).unwrap();
49
50        assert!(toml_string.contains(crate_name));
51    }
52}