1 use std::fs::File;
2 use std::path::{Path, PathBuf};
3 use std::{env, fs, io};
4 
5 use crate::errors::*;
6 use crate::iter::Iter;
7 
8 pub struct Finder<'a> {
9   filename:  &'a Path,
10 }
11 
12 impl<'a> Finder<'a> {
new() -> Self13     pub fn new() -> Self {
14         Finder {
15             filename: Path::new(".env"),
16         }
17     }
18 
filename(mut self, filename: &'a Path) -> Self19     pub fn filename(mut self, filename: &'a Path) -> Self {
20         self.filename = filename;
21         self
22     }
23 
find(self) -> Result<(PathBuf, Iter<File>)>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.
find(directory: &Path, filename: &Path) -> Result<PathBuf>33 pub fn find(directory: &Path, filename: &Path) -> Result<PathBuf> {
34     let candidate = directory.join(filename);
35 
36     match fs::metadata(&candidate) {
37         Ok(metadata) => if metadata.is_file() {
38             return Ok(candidate);
39         },
40         Err(error) => {
41             if error.kind() != io::ErrorKind::NotFound {
42                 return Err(Error::Io(error));
43             }
44         }
45     }
46 
47     if let Some(parent) = directory.parent() {
48         find(parent, filename)
49     } else {
50         Err(Error::Io(io::Error::new(io::ErrorKind::NotFound, "path not found")))
51     }
52 }
53