file_to_map/
lib.rs

1//! # File To Map
2//! A crate that parses your file to a hashmap-base struct.
3//! Can be used to parse configuration file easily.
4
5use std::collections::HashMap;
6use std::fs;
7use std::io::{Error, ErrorKind};
8use std::ops::Index;
9
10const DEFAULT_KEY_VALUE_SEP: &str = "=";
11const DEFAULT_PAIR_SEP: &str = "\n";
12
13/// File To Map struct
14pub struct FileToMap<'a> {
15    key_value_sep: &'a str,
16    pair_sep: &'a str,
17    map: HashMap<String, String>,
18    file_name: &'a str,
19}
20
21impl<'a> FileToMap<'a> {
22    /// Creates a new struct. The file won't be parsed until calling the build function.
23    pub fn new(file_name: &'a str) -> Self {
24        FileToMap {
25            key_value_sep: DEFAULT_KEY_VALUE_SEP,
26            pair_sep: DEFAULT_PAIR_SEP,
27            map: HashMap::new(),
28            file_name: file_name,
29        }
30    }
31
32    /// Changes the key-value pair separator. The default is `=`.
33    pub fn set_key_value_separator(mut self, sep: &'a str) -> Self {
34        self.key_value_sep = sep;
35        return self;
36    }
37
38    /// Changes the pairs separator. The default is just a new line, but can be changed to any
39    /// string.
40    pub fn set_pair_separator(mut self, sep: &'a str) -> Self {
41        self.pair_sep = sep;
42        return self;
43    }
44
45    /// This function parses the file and creates the map. Call this function after changing the
46    /// separators (if necessary). Be aware - this function returns a result struct. it will return an error due to an IO file error or missing key-value separator.
47    pub fn build(mut self) -> Result<Self, Error> {
48        let data = fs::read_to_string(self.file_name)?; // reading the file
49        let data = self.strip_trailing_newline(&data); // remove the new-line in the end of the file
50
51        let splitted_data: Vec<String> =
52            data.split(self.pair_sep).map(|s| String::from(s)).collect(); // split by pairs separator
53
54        let data_parse_result = splitted_data
55            .iter()
56            .try_for_each(|s| -> std::io::Result<()> {
57                let key_val: Vec<&str> = s.splitn(2, self.key_value_sep).collect(); // split to key and value
58                if key_val.len() != 2 {
59                    // will happen when cannot find key-value separator
60                    return Err(Error::new(
61                        ErrorKind::Other,
62                        format!("Cannot find {} in {}", self.key_value_sep, s),
63                    ));
64                }
65                self.map
66                    .insert(String::from(key_val[0]), String::from(key_val[1])); // add the pair to map
67                Ok(())
68            });
69
70        let ret: Result<Self, Error> = match data_parse_result {
71            Ok(()) => Ok(self),
72            Err(e) => Err(e),
73        };
74
75        ret
76    }
77
78    /// Get value by key, same as get() of HashMap struct.
79    pub fn get(&self, key: &str) -> Option<&String> {
80        self.map.get(key)
81    }
82    /// This function removes the trailing newline. Some of the text editors in unix-based systems adding this.
83    fn strip_trailing_newline(&self, input: &'a str) -> &'a str {
84        input
85            .strip_suffix("\r\n")
86            .or(input.strip_suffix("\n"))
87            .unwrap_or(input)
88    }
89}
90
91impl<'a> Index<&str> for FileToMap<'a> {
92    type Output = String;
93    /// Get value by key, using ["key"] (index operator).
94    ///
95    /// # Panic
96    /// The function will panic when the key isn't in the map.
97    fn index(&self, key: &str) -> &String {
98        self.map
99            .get(key)
100            .expect(format!("Cannot find {} in FileToMap", key).as_str())
101    }
102}
103#[cfg(test)]
104mod test;