use serde_yaml;
use std::fs::File;
use commit::Commit;
use std::env::current_dir;
use handlebars::Handlebars;
use std::io::{Error, ErrorKind, Read, BufReader};
pub const FILE: &str = ".changelog.yml";
#[serde(default)]
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Tag {
pub keyword: String,
pub title: String,
}
#[serde(default)]
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct PostProcessor {
pub lookup: String,
pub replace: String,
}
#[serde(default)]
#[derive(Serialize, Deserialize, Default, Debug)]
pub struct Configuration {
pub categories: Vec<Tag>,
pub scopes: Vec<Tag>,
pub title: String,
pub template: String,
pub date_format: String,
pub post_processors: Vec<PostProcessor>,
}
pub fn from(filename: &Option<String>) -> Result<Configuration, Error> {
let mut config: Configuration = match *filename {
None => {
info!("Using default built-in configuration");
serde_yaml::from_str(include_str!("../resources/config.yml"))
}
Some(ref file) => {
info!("Using configuration from {}", file);
serde_yaml::from_reader(File::open(file)?)
}
}.map_err(|e| {
error!("Invalid configuration file '{:?}', {}.", filename, e);
Error::from(ErrorKind::InvalidInput)
})?;
let template = if config.template.is_empty() {
String::from(include_str!("../resources/report.handlebars"))
} else {
let mut template = String::new();
let file = File::open(config.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.template = template;
if config.date_format.is_empty() {
config.date_format = "%Y-%m-%d %H:%M".to_string()
}
if config.scopes.is_empty() {
config.scopes.push(Tag::default());
}
if config.categories.is_empty() {
config.categories.push(Tag::default());
}
debug!("CONFIG: {:#?}", &config);
Ok(config)
}
pub fn report_title(tags: &[Tag], given: &Option<String>) -> Option<String> {
let keyword = given.clone().unwrap_or_default();
for tag in tags {
if tag.keyword == keyword {
return Some(tag.title.clone());
}
}
None
}
pub fn is_interesting(config: &Configuration, commit: &Commit) -> bool {
for line in &commit.lines {
if line.text.is_none() {
continue;
}
if report_title(&config.scopes, &line.scope).is_none() {
continue;
}
if report_title(&config.categories, &line.category).is_none() {
continue;
}
return true;
}
info!("Commit {} is not interesting", commit.summary);
false
}
pub fn find_file(given: Option<&str>) -> Option<String> {
if given.is_some() {
return given.map(str::to_string);
}
if let Ok(mut cwd) = current_dir() {
while cwd.exists() {
cwd.push(FILE);
if cwd.is_file() {
return Some(cwd.to_string_lossy().to_string());
}
cwd.pop();
if cwd.parent().is_some() {
cwd.pop();
} else {
break;
}
}
}
None
}