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
use std::fs::File;
use std::io::{self, BufRead, BufReader};
use std::path::Path;

#[derive(Debug, Error)]
pub enum LoaderError {
    #[error("error reading line in loader conf")]
    Line(#[source] io::Error),
    #[error("loader conf is not a file")]
    NotAFile,
    #[error("default was defined without a value")]
    NoValueForDefault,
    #[error("timeout was defined without a value")]
    NoValueForTimeout,
    #[error("error opening loader file")]
    Open(#[source] io::Error),
    #[error("timeout was defined with a value ({}) which is not a number", _0)]
    TimeoutNaN(String),
}

#[derive(Debug, Default, Clone)]
pub struct LoaderConf {
    pub default: Option<Box<str>>,
    pub timeout: Option<u32>,
}

impl LoaderConf {
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, LoaderError> {
        let path = path.as_ref();

        let mut loader = LoaderConf::default();
        if !path.exists() {
            return Ok(loader);
        }

        if !path.is_file() {
            return Err(LoaderError::NotAFile);
        }

        let file = File::open(path).map_err(LoaderError::Open)?;

        for line in BufReader::new(file).lines() {
            let line = line.map_err(LoaderError::Line)?;
            let mut fields = line.split_whitespace();
            match fields.next() {
                Some("default") => match fields.next() {
                    Some(default) => loader.default = Some(default.into()),
                    None => return Err(LoaderError::NoValueForDefault),
                },
                Some("timeout") => match fields.next() {
                    Some(timeout) => {
                        if let Ok(timeout) = timeout.parse::<u32>() {
                            loader.timeout = Some(timeout);
                        } else {
                            return Err(LoaderError::TimeoutNaN(timeout.into()));
                        }
                    }
                    None => return Err(LoaderError::NoValueForTimeout),
                },
                _ => (),
            }
        }

        Ok(loader)
    }
}