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 from_path<P:'a + AsRef<Path>>(path: &'a P) -> Self {
20 Finder {
21 filename: path.as_ref().clone(),
22 }
23 }
24
25 pub fn filename(mut self, filename: &'a Path) -> Self {
26 self.filename = filename;
27 self
28 }
29
30 pub fn find(self) -> Result<(PathBuf, Iter<File>)> {
31 let path = find(&env::current_dir().map_err(Error::Io)?, self.filename)?;
32 let file = File::open(&path).map_err(Error::Io)?;
33 let iter = Iter::new(file);
34 Ok((path, iter))
35 }
36}
37
38pub fn find(directory: &Path, filename: &Path) -> Result<PathBuf> {
40 let candidate = directory.join(filename);
41
42 match fs::metadata(&candidate) {
43 Ok(metadata) => {
44 if metadata.is_file() {
45 return Ok(candidate);
46 }
47 }
48 Err(error) => {
49 if error.kind() != io::ErrorKind::NotFound {
50 return Err(Error::Io(error));
51 }
52 }
53 }
54
55 if let Some(parent) = directory.parent() {
56 find(parent, filename)
57 } else {
58 Err(Error::Io(io::Error::new(
59 io::ErrorKind::NotFound,
60 "path not found",
61 )))
62 }
63}