gitjournal/
config.rs

1//! Everything related to the git-journal configuration. The configuration
2//! files are stored in [toml](https://github.com/toml-lang/toml) format with the file name `.gitjournal.toml`.
3
4use toml;
5
6use failure::{format_err, Error};
7use log::info;
8use serde_derive::{Deserialize, Serialize};
9use std::{fs::File, io::prelude::*, path::PathBuf};
10
11/// The configuration structure for git-journal.
12#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct Config {
14    /// Specifies the available categories for the commit message
15    pub categories: Vec<String>,
16
17    /// Set the characters where the categories are wrapped in
18    pub category_delimiters: Vec<String>,
19
20    /// Set to false if the output should not be colored
21    pub colored_output: bool,
22
23    /// Specifies the default template. Will be used for tag validation and
24    /// printing.
25    pub default_template: Option<String>,
26
27    /// Show or hide the debug messages like `[OKAY] ...` or `[INFO] ...`
28    pub enable_debug: bool,
29
30    /// Excluded tags in an array, e.g. "internal"
31    pub excluded_commit_tags: Vec<String>,
32
33    /// Enable or disable the output and accumulation of commit footers
34    pub enable_footers: bool,
35
36    /// Show or hide the commit hash for every entry
37    pub show_commit_hash: bool,
38
39    /// Show or hide the commit message prefix, e.g. JIRA-1234
40    pub show_prefix: bool,
41
42    /// Sort the commits during the output by "date" (default) or "name"
43    pub sort_by: String,
44
45    /// Commit message template prefix which will be added during commit
46    /// preparation
47    pub template_prefix: String,
48}
49
50impl Config {
51    /// Constructs a new `Config` with default values.
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// use gitjournal::Config;
57    /// let config = Config::new();
58    /// ```
59    pub fn new() -> Self {
60        Config {
61            categories: Self::get_default_categories(),
62            category_delimiters: vec!["[".to_owned(), "]".to_owned()],
63            colored_output: true,
64            default_template: None,
65            enable_debug: true,
66            excluded_commit_tags: vec![],
67            enable_footers: false,
68            show_commit_hash: false,
69            show_prefix: false,
70            sort_by: "date".to_owned(),
71            template_prefix: "JIRA-1234".to_owned(),
72        }
73    }
74
75    fn get_default_categories() -> Vec<String> {
76        vec![
77            "Added".to_owned(),
78            "Changed".to_owned(),
79            "Fixed".to_owned(),
80            "Improved".to_owned(),
81            "Removed".to_owned(),
82        ]
83    }
84
85    /// Save the default configuration file in a certain path.
86    ///
87    /// # Examples
88    ///
89    /// ```
90    /// use gitjournal::Config;
91    /// Config::new()
92    ///     .save_default_config(".")
93    ///     .expect("Could not save config.");
94    /// ```
95    ///
96    /// # Errors
97    /// When toml encoding or file creation failed.
98    pub fn save_default_config(&self, path: &str) -> Result<String, Error> {
99        // Serialize self to toml
100        let toml_string = toml::to_string(&self).unwrap();
101        info!("{:?}", toml_string);
102
103        // Get the correct path
104        let path_buf = self.get_path_with_filename(path);
105        let path_string = path_buf
106            .to_str()
107            .ok_or_else(|| format_err!("Cannot convert path to string"))?;
108
109        // Write the path to string
110        let mut file = File::create(&path_buf)?;
111        file.write_all(toml_string.as_bytes())?;
112        Ok(path_string.to_owned())
113    }
114
115    /// Load a configuration file from a certain path.
116    ///
117    /// # Examples
118    ///
119    /// ```
120    /// use gitjournal::Config;
121    /// Config::new().load(".").expect("Could not load config.");
122    /// ```
123    ///
124    /// # Errors
125    /// When toml decoding or file opening failed.
126    pub fn load(&mut self, path: &str) -> Result<(), Error> {
127        let path_buf = self.get_path_with_filename(path);
128        let mut file = File::open(&path_buf)?;
129        let mut toml_string = String::new();
130        file.read_to_string(&mut toml_string)?;
131
132        // Deserialize the toml string
133        *self = toml::from_str(&toml_string)?;
134
135        // If the categories are not found within the toml it will return an
136        // empty array which will break the parser. So use the default
137        // ones instead.
138        if self.categories.is_empty() {
139            self.categories = Self::get_default_categories();
140        }
141        Ok(())
142    }
143
144    /// Check if the configuration matches with the default one.
145    ///
146    /// # Examples
147    ///
148    /// ```
149    /// use gitjournal::Config;
150    /// assert_eq!(Config::new().is_default_config(), true);
151    /// ```
152    pub fn is_default_config(&self) -> bool {
153        *self == Config::new()
154    }
155
156    fn get_path_with_filename(&self, path: &str) -> PathBuf {
157        let mut path_buf = PathBuf::from(path);
158        path_buf.push(".gitjournal.toml");
159        path_buf
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166
167    #[test]
168    fn config_save_and_load_ok() {
169        let mut config = Config::new();
170        assert!(config.save_default_config(".").is_ok());
171        assert!(config.load(".").is_ok());
172        assert_eq!(config.is_default_config(), true);
173    }
174
175    #[test]
176    fn config_save_err() {
177        let config = Config::new();
178        let res = config.save_default_config("/dev/null");
179        assert!(res.is_err());
180        if let Err(e) = res {
181            println!("{}", e);
182        }
183    }
184
185    fn load_and_print_failure(path: &str) {
186        let mut config = Config::new();
187        let res = config.load(path);
188        assert!(res.is_err());
189        if let Err(e) = res {
190            println!("{}", e);
191        }
192    }
193
194    #[test]
195    fn config_load_err() {
196        load_and_print_failure("/dev/null");
197    }
198
199    #[test]
200    fn config_load_invalid_1() {
201        load_and_print_failure("tests/invalid_1.toml");
202    }
203
204    #[test]
205    fn config_load_invalid_2() {
206        load_and_print_failure("tests/invalid_2.toml");
207    }
208
209    #[test]
210    fn config_load_invalid_3() {
211        load_and_print_failure("tests/invalid_3.toml");
212    }
213}