culper_lib/config/
mod.rs

1use dirs;
2use failure::{Context, Error, Fail, ResultExt};
3use std::fs::File;
4use std::fs::OpenOptions;
5use std::io::prelude::*;
6use std::path::PathBuf;
7use toml;
8
9#[derive(Serialize, Deserialize, Debug, Clone)]
10pub struct CulperConfig {
11    pub targets: Option<Vec<TargetConfig>>,
12    pub owners: Option<Vec<UserConfig>>,
13    pub admins: Option<Vec<UserConfig>>,
14    pub me: UserConfig,
15}
16
17#[derive(Serialize, Deserialize, Debug, Clone)]
18pub struct UserConfig {
19    pub fingerprint: String,
20    pub name: String,
21}
22
23#[derive(Serialize, Deserialize, Debug, Clone)]
24pub struct TargetConfig {
25    pub id: String,
26    pub host: String,
27}
28
29#[derive(Debug, Clone)]
30pub struct ConfigReader {
31    pub path: PathBuf,
32    pub config: Option<CulperConfig>,
33}
34
35impl ConfigReader {
36    pub fn new(raw_config_path: Option<&str>) -> ConfigReader {
37        let config_path = match raw_config_path {
38            Some(val) => PathBuf::from(val),
39            None => get_config_path(),
40        };
41
42        ConfigReader {
43            path: config_path,
44            config: None,
45        }
46    }
47
48    pub fn read(&mut self) -> Result<CulperConfig, Error> {
49        if !&self.path.exists() {
50            return Err(format_err!(
51                "{} not found. Create one or pass the --config_file option.",
52                &self
53                    .path
54                    .to_str()
55                    .expect("Failed converting path to string.")
56            ));
57        }
58
59        let mut raw_toml = String::new();
60        File::open(&self.path)
61            .context("Could not open configuration file")?
62            .read_to_string(&mut raw_toml)
63            .context("Could not read configuration file")?;
64
65        let config = self.read_string_to_config(&raw_toml)?;
66        self.config = Some(config.clone());
67        Ok(config)
68    }
69
70    pub fn add_target(&mut self, host: &str, id: &str) -> Result<(), Error> {
71        match &mut self.config {
72            Some(ref mut config) => match config.targets {
73                None => {
74                    config.targets = Some(vec![TargetConfig {
75                        host: host.to_owned(),
76                        id: id.to_owned(),
77                    }]);
78                    Ok(())
79                }
80                Some(ref mut targets) => {
81                    targets.push(TargetConfig {
82                        host: host.to_owned(),
83                        id: id.to_owned(),
84                    });
85
86                    Ok(())
87                }
88            },
89            None => Err(format_err!("Config is not set.")),
90        }
91    }
92
93    pub fn update(&mut self, new_config: CulperConfig) -> &mut Self {
94        self.config = Some(new_config);
95        self
96    }
97
98    pub fn write(&self) -> Result<(), Error> {
99        match &self.config {
100            Some(config) => {
101                OpenOptions::new()
102                    .write(true)
103                    .create(true)
104                    .open(&self.path)?
105                    .write_all(toml::to_string(&config)?.as_bytes())?;
106                Ok(())
107            }
108            None => Err(format_err!("No config available to write.")),
109        }
110    }
111
112    fn read_string_to_config(&self, string: &str) -> Result<CulperConfig, Error> {
113        let parsed_toml: CulperConfig = toml::from_str(&string)?;
114        Ok(parsed_toml)
115    }
116}
117
118fn get_config_path() -> PathBuf {
119    let mut path = PathBuf::new();
120    match dirs::home_dir() {
121        Some(home) => path.push(home),
122        None => path.push("./"),
123    };
124    path.push(".culper.toml");
125    path
126}
127
128pub fn create(name: String, fingerprint: String, config_path: String) -> Result<(), Error> {
129    let config = CulperConfig {
130        me: UserConfig { name, fingerprint },
131        targets: None,
132        owners: None,
133        admins: None,
134    };
135    File::create(config_path)?.write_all(toml::to_string(&config)?.as_bytes())?;
136    Ok(())
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142    use std::path::Path;
143
144    #[test]
145    fn can_create_config() {
146        create(
147            "test@test.de".to_owned(),
148            "12345678".to_owned(),
149            "./culper.toml".to_owned(),
150        )
151        .unwrap();
152        assert!(Path::new("./culper.toml").exists());
153    }
154
155    #[test]
156    fn can_update_existing_config() {
157        let mut config_reader = ConfigReader::new(Some("./culper.toml"));
158
159        config_reader.update(CulperConfig {
160            me: UserConfig {
161                name: "overwrite@mail.de".to_owned(),
162                fingerprint: "1234 5678 ABCD ETC".to_owned(),
163            },
164            targets: None,
165            owners: None,
166            admins: None,
167        });
168
169        config_reader
170            .add_target("www.test.de", "alskjdflsajfd")
171            .unwrap();
172        config_reader.write().unwrap();
173
174        let mut file = OpenOptions::new().read(true).open("./culper.toml").unwrap();
175        let mut contents = String::new();
176        file.read_to_string(&mut contents).unwrap();
177
178        assert_eq!(contents, ::toml::to_string(&config_reader.config).unwrap())
179    }
180}