dinglebit_config/
simple.rs1use std::collections::HashMap;
4use std::fs::read_to_string;
5
6use crate::Config;
7
8#[derive(Debug, PartialEq)]
9pub struct Simple {
10 values: HashMap<String, String>,
11}
12
13#[derive(Debug, PartialEq)]
14pub enum Error {
15 File(String),
16 InvalidKeyValuePair,
17}
18
19fn parse_line(line: &str) -> Result<Option<(String, String)>, Error> {
20 let line = line.trim();
22 if line.starts_with("#") {
23 return Ok(None);
24 } else if line.len() < 1 {
25 return Ok(None);
26 }
27
28 let parts: Vec<&str> = line.splitn(2, "=").collect();
30 if parts.len() < 2 {
31 return Err(Error::InvalidKeyValuePair);
32 }
33
34 Ok(Some((
35 parts[0].trim().to_string(),
36 parts[1].trim().to_string(),
37 )))
38}
39
40fn parse(s: &str) -> Result<HashMap<String, String>, Error> {
41 let mut values = HashMap::new();
42
43 for line in s.split("\n") {
44 match parse_line(&line) {
45 Err(e) => return Err(e),
46 Ok(v) => match v {
47 None => continue,
48 Some(s) => {
49 values.insert(s.0, s.1);
50 }
51 },
52 }
53 }
54
55 Ok(values)
56}
57
58impl Simple {
59 pub fn from_str(s: &str) -> Result<Self, Error> {
73 Ok(Self { values: parse(s)? })
74 }
75
76 pub fn from_file(path: &str) -> Result<Self, Error> {
79 let file = match read_to_string(path) {
80 Ok(s) => s,
81 Err(e) => return Err(Error::File(e.to_string())),
82 };
83 Ok(Self {
84 values: parse(&file)?,
85 })
86 }
87}
88
89impl Config for Simple {
90 fn get(&self, key: &str) -> Option<String> {
91 match self.values.get(key) {
92 Some(value) => Some(value.to_string()),
93 None => None,
94 }
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use crate::simple::{parse_line, Error, Simple};
101 use crate::Config;
102
103 use std::array::IntoIter;
104 use std::collections::HashMap;
105 use std::iter::FromIterator;
106
107 #[test]
108 fn test_parse_line() {
109 let tests =
110 HashMap::<&str, Result<Option<(String, String)>, Error>>::from_iter(IntoIter::new([
111 (" # comment ", Ok(None)),
112 (" test", Err(Error::InvalidKeyValuePair)),
113 (
114 " foo = bar ",
115 Ok(Some(("foo".to_string(), "bar".to_string()))),
116 ),
117 ]));
118 tests.iter().for_each(|(k, v)| {
119 assert_eq!(parse_line(k), *v);
120 });
121 }
122
123 #[test]
124 fn test_file() {
125 let exp: Result<Simple, Error> = Err(Error::File(
127 "No such file or directory (os error 2)".to_string(),
128 ));
129 assert_eq!(Simple::from_file("/i/hope/i/do/not/exist.cfg"), exp);
130
131 let cfg = match Simple::from_file("example.cfg") {
133 Err(e) => panic!("reading 'example.cfg': {:?}", e),
134 Ok(cfg) => cfg,
135 };
136 assert_eq!(cfg.get("foo"), Some("bar".to_string()));
137 assert_eq!(cfg.get("list"), Some("one, two, three".to_string()));
138 }
139}