devela/sys/path/
project.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// devela::sys::path::project
//
//!
//

#[cfg(not(miri))]
use std::{
    convert::AsRef,
    env, fs, io,
    path::{Path, PathBuf},
};

/// Returns an absolute [`PathBuf`], relative to the `crate`'s root.
///
/// It determines the root by finding the first `Cargo.toml` file, from the
/// current directory through its ancestors.
///
/// # Errors
/// Returns an error if it can't find any `Cargo.toml` file,
/// or if it encounters an invalid path during the search process.
///
/// # Examples
/// ```
/// use devela::sys::crate_root;
///
/// match crate_root("") {
///     Ok(p) => println!("Current crate root is {:?}", p),
///     Err(e) => println!("Error obtaining crate root {:?}", e)
/// };
/// ```
#[cfg(not(miri))] // unsupported operation: `getcwd` not available when isolation is enabled
pub fn crate_root<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
    let current_path = env::current_dir()?;
    let mut root_path = current_path.clone();

    for p in current_path.as_path().ancestors() {
        let has_cargo = fs::read_dir(p)?.any(|p| p.unwrap().file_name() == *"Cargo.toml");
        if has_cargo {
            return Ok(root_path.join(path.as_ref()));
        }
        root_path.pop();
    }
    Err(io::Error::new(io::ErrorKind::NotFound, "Ran out of places to find Cargo.toml"))
}

/// Like [`crate_root`] but returns a [`String`].
///
/// In case of an error the returned string will be empty.
#[cfg(not(miri))]
#[must_use]
#[inline]
pub fn crate_root_string<P: AsRef<Path>>(path: P) -> String {
    crate_root(Path::new(path.as_ref())).map_or(String::new(), |p| p.to_str().unwrap().to_owned())
}