dotenvy/
find.rs

1use std::fs::File;
2use std::path::{Path, PathBuf};
3use std::{env, fs, io};
4
5use crate::errors::*;
6use crate::iter::Iter;
7
8pub struct Finder<'a> {
9    filename: &'a Path,
10}
11
12impl<'a> Finder<'a> {
13    pub fn new() -> Self {
14        Finder {
15            filename: Path::new(".env"),
16        }
17    }
18
19    pub fn filename(mut self, filename: &'a Path) -> Self {
20        self.filename = filename;
21        self
22    }
23
24    pub fn find(self) -> Result<(PathBuf, Iter<File>)> {
25        let path = find(&env::current_dir().map_err(Error::Io)?, self.filename)?;
26        let file = File::open(&path).map_err(Error::Io)?;
27        let iter = Iter::new(file);
28        Ok((path, iter))
29    }
30}
31
32/// Searches for `filename` in `directory` and parent directories until found or root is reached.
33pub fn find(directory: &Path, filename: &Path) -> Result<PathBuf> {
34    let candidate = directory.join(filename);
35
36    match fs::metadata(&candidate) {
37        Ok(metadata) => {
38            if metadata.is_file() {
39                return Ok(candidate);
40            }
41        }
42        Err(error) => {
43            if error.kind() != io::ErrorKind::NotFound {
44                return Err(Error::Io(error));
45            }
46        }
47    }
48
49    if let Some(parent) = directory.parent() {
50        find(parent, filename)
51    } else {
52        Err(Error::Io(io::Error::new(
53            io::ErrorKind::NotFound,
54            "path not found",
55        )))
56    }
57}