micro_kit/
config.rs

1use std::fmt;
2use std::error::Error as StdError;
3use std::borrow::Cow;
4use std::fs::File;
5use std::io;
6use std::io::prelude::Read;
7use std::ops::Index;
8
9use ::yaml::YamlLoader;
10use ::yaml::Yaml;
11use ::yaml::scanner::ScanError;
12use ::yaml::emitter::EmitError;
13
14/// When loading a YAML file there maybe missing required components or bad YAML. This error type
15/// allows these errors to be propegated.
16#[derive(Debug)]
17pub enum ConfigError {
18    /// A problem finding or reading the config file.
19    IoError(io::Error),
20    /// When a yaml file is missing a required component. Will return the missing component.
21    MissingComponent(String),
22    /// When a YAML file encounters a parsing error.
23    YamlSyntax(ScanError),
24    /// When there is a problem emitting yaml.
25    YamlEmit(EmitError),
26    /// Handle a Box<Error>
27    Error(Box<StdError>),
28}
29
30impl fmt::Display for ConfigError {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        match *self {
33            ConfigError::IoError(ref err) => write!(f, "ConfigError: {}", err),
34            ConfigError::MissingComponent(ref comp) => write!(f, "ConfigError: Missing {}", comp),
35            ConfigError::YamlSyntax(ref err) => write!(f, "ConfigError: {}", err),
36            ConfigError::YamlEmit(ref err) => write!(f, "ConfigError: {:?}", err),
37            ConfigError::Error(ref err) => write!(f, "ConfigError: {:?}", err),
38        }
39    }
40}
41
42impl StdError for ConfigError {
43    fn description(&self) -> &str {
44        match *self {
45            ConfigError::IoError(ref err) => err.description(),
46            ConfigError::MissingComponent(_) => "Missing Required Component",
47            ConfigError::YamlSyntax(ref err) => err.description(),
48            ConfigError::YamlEmit(_) => "Problem emitting Yaml",
49            ConfigError::Error(ref err) => err.description(),
50        }
51    }
52
53    fn cause(&self) -> Option<&StdError> {
54        match *self {
55            ConfigError::IoError(ref err) => Some(err),
56            ConfigError::MissingComponent(_) => None,
57            ConfigError::YamlSyntax(ref err) => Some(err),
58            ConfigError::YamlEmit(_) => None,
59            ConfigError::Error(_) => None,
60        }
61    }
62}
63
64impl From<io::Error> for ConfigError {
65    fn from(err: io::Error) -> ConfigError {
66        ConfigError::IoError(err)
67    }
68}
69
70impl From<ScanError> for ConfigError {
71    fn from(err: ScanError) -> ConfigError {
72        ConfigError::YamlSyntax(err)
73    }
74}
75
76impl From<EmitError> for ConfigError {
77    fn from(err: EmitError) -> ConfigError {
78        ConfigError::YamlEmit(err)
79    }
80}
81
82impl From<Box<StdError>> for ConfigError {
83    fn from(err: Box<StdError>) -> ConfigError {
84        ConfigError::Error(err)
85    }
86}
87
88/// Wrapper for a parsed configuration. Should wrap the Yaml better to make the API easier to use.
89#[derive(Debug, Clone)]
90pub struct ConfigFile {
91    yaml: Yaml,
92}
93
94impl ConfigFile {
95    /// Open a yaml file as a config file.
96    pub fn open<'a>(name: Cow<'a, str>) -> Result<Self, ConfigError> {
97        let mut config_file = File::open(name.as_ref())?;
98        let mut config_str = String::new();
99        config_file.read_to_string(&mut config_str).expect("Error reading config file");
100        let yamls = YamlLoader::load_from_str(&config_str)?;
101        let yaml: Yaml = yamls[0].clone();
102        Ok(ConfigFile {
103            yaml: yaml
104        })
105    }
106}
107
108static BAD_VALUE: Yaml = Yaml::BadValue;
109
110/// Allow a `ConfigFile` to be accessed like a map
111impl<'a> Index<&'a str> for ConfigFile {
112    type Output = Yaml;
113
114    fn index(&self, idx: &'a str) -> &Yaml {
115        let key = Yaml::String(idx.to_owned());
116        match self.yaml.as_hash() {
117            Some(h) => h.get(&key).unwrap_or(&BAD_VALUE),
118            None => &BAD_VALUE
119        }
120    }
121}
122
123/// Allow a `ConfigFile` to be accessed like an array.
124impl Index<usize> for ConfigFile {
125    type Output = Yaml;
126
127    fn index(&self, idx: usize) -> &Yaml {
128        if let Some(v) = self.yaml.as_vec() {
129            v.get(idx).unwrap_or(&BAD_VALUE)
130        } else if let Some(v) = self.yaml.as_hash() {
131            let key = Yaml::Integer(idx as i64);
132            v.get(&key).unwrap_or(&BAD_VALUE)
133        } else {
134            &BAD_VALUE
135        }
136    }
137}