use serde_yaml;
use std::fs::File;
use commit::{Commit, Line};
use handlebars::Handlebars;
use std::collections::HashMap;
use std::io::{Error, ErrorKind, Read, BufReader};
#[serde(default)]
#[derive(Serialize, Deserialize, Default)]
pub struct Configuration {
pub categories: TagConfiguration,
pub scopes: TagConfiguration,
pub report: ReportConfiguration,
}
#[serde(default)]
#[derive(Serialize, Deserialize, Default)]
pub struct TagConfiguration {
pub tags: Vec<String>,
pub titles: HashMap<String, String>,
pub default: String,
}
#[serde(default)]
#[derive(Serialize, Deserialize, Default)]
pub struct ReportConfiguration {
pub title: String,
pub template: String,
pub date_format: String,
}
pub fn from(filename: Option<&str>) -> Result<Configuration, Error> {
let cfg = match filename {
None => serde_yaml::from_str(include_str!("../resources/config.yml")),
Some(file) => serde_yaml::from_reader(File::open(file)?),
};
let mut config: Configuration = cfg.map_err(|e| {
error!(
"Invalid configuration file '{}', {}.",
filename.unwrap_or("default"),
e
);
Error::from(ErrorKind::InvalidInput)
})?;
let template = if config.report.template.is_empty() {
String::from(include_str!("../resources/report.handlebars"))
} else {
let mut template = String::new();
let file = File::open(config.report.template)?;
BufReader::new(file).read_to_string(&mut template)?;
template
};
Handlebars::new()
.register_template_string("t", &template)
.map_err(|e| {
error!("Invalid handlebar template: '{}'", e);
Error::from(ErrorKind::InvalidInput)
})?;
config.report.template = template;
if config.report.date_format.is_empty() {
config.report.date_format = String::from("%Y-%m-%d");
}
Ok(config)
}
impl Configuration {
pub fn validate(&self, line: &Line) -> Line {
let text = line.text.clone();
let scope = self.scopes.validate(line.scope.clone());
let category = self.categories.validate(line.category.clone());
Line {
text,
category,
scope,
}
}
pub fn is_interesting(&self, commit: &Commit) -> bool {
for line in &commit.lines {
if line.text.is_none() {
continue;
}
if self.scopes.validate(line.scope.clone()).is_none() {
continue;
}
if self.categories.validate(line.category.clone()).is_none() {
continue;
}
return true;
}
info!("Commit {} is not interesting", commit.summary);
false
}
}
impl TagConfiguration {
pub fn validate(&self, given: Option<String>) -> Option<String> {
given
.or_else(|| Some(self.default.clone()))
.and_then(|tag| if self.tags.iter().any(|t| t == &tag) {
Some(tag)
} else {
None
})
}
pub fn get_title(&self, item: &str) -> String {
match self.titles.get(item) {
Some(text) => text,
None => item,
}.to_string()
}
pub fn show(&self, item: &str, warn_if_empty: bool) {
if self.tags.is_empty() {
if warn_if_empty {
warn!("No {} tags defined", item);
}
} else {
info!("Available {} tags: {:?}", item, self.tags);
if self.default.is_empty() {
warn!("No default {} tag defined.", item);
} else {
info!(r#"Using "{}" as the default {} tag"#, self.default, item);
}
}
for t in &self.tags {
match self.titles.get(t) {
None => warn!(r#"Title for {} tag "{}" is missing"#, item, t),
Some(title) => debug!(r#"Title for {} tag "{}" is "{}""#, item, t, title),
};
}
}
}