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}