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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
use log::*;
use std::ffi::OsString;
use std::path::PathBuf;

fn find_it_in(to_find: &str, path: PathBuf) -> Option<PathBuf> {
    debug!("Searching {:?} for {}", path, to_find);
    if path.is_dir() {
        match path.read_dir() {
            Ok(read_dir) => {
                let mut i = read_dir.into_iter();
                while let Some(Ok(entry)) = i.next() {
                    if let Some(f) = find_it_in(to_find, entry.path()) {
                        return Some(f);
                    }
                }
            }
            Err(e) => {
                warn!("Could not search for {} in {:?}: {:?}", to_find, path, e);
            }
        }
    } else if path.is_file() {
        if let Some(os_str) = path.file_name() {
            if os_str == to_find {
                return Some(path);
            }
        }
    }
    None
}

fn find_it(path: OsString, to_find: &str) -> Option<PathBuf> {
    std::env::split_paths(&path)
        .filter_map(|dir| find_it_in(to_find, dir))
        .next()
}

fn locate_header_from_package(
    path: OsString,
    header_name: &str,
    package: Package,
) -> Option<PathBuf> {
    debug!(
        "Checking package {} @ {} for {}",
        package.name, package.version, header_name
    );
    if let Ok(library) = pkg_config::Config::new()
        .atleast_version(&package.version)
        .probe(&package.name)
    {
        for include_path in library.include_paths {
            if let Some(f) = find_it_in(header_name, include_path) {
                return Some(f);
            }
        }
    }
    debug!("Could not find package, checking path");
    if let Some(f) = find_it(path, header_name) {
        return Some(f);
    }
    None
}

pub struct Package {
    pub version: String,
    pub name: String,
}

pub fn locate_header(header_name: &str, package: Option<Package>) -> Option<PathBuf> {
    let path = std::env::var_os("PATH").expect("No path defined");
    debug!("Locating header using path {:?}", path);
    locate_header_with_path(path, header_name, package)
}

pub fn locate_header_with_path(
    path: OsString,
    header_name: &str,
    package: Option<Package>,
) -> Option<PathBuf> {
    if let Some(p) = package {
        return locate_header_from_package(path, header_name, p);
    } else {
        return find_it(path, header_name);
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn locate_resource_header() {
        let _ = env_logger::try_init();

        let cargo_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
        assert!(locate_header_with_path(cargo_dir.into_os_string(), "to_find.h", None).is_some());
    }
}