use crates::rayon::prelude::*;
use impl_prelude::*;
use std::path::Path;
#[derive(Debug)]
pub enum ChangelogStyle {
Directory {
path: String,
extension: Option<String>,
},
File {
path: String,
},
}
impl ChangelogStyle {
pub fn file<P>(path: P) -> Self
where P: ToString,
{
ChangelogStyle::File {
path: path.to_string(),
}
}
pub fn directory<P>(path: P, ext: Option<String>) -> Self
where P: ToString,
{
ChangelogStyle::Directory {
path: path.to_string(),
extension: ext,
}
}
fn describe(&self) -> String {
match *self {
ChangelogStyle::Directory { ref path, ref extension } => {
if let Some(ext) = extension.as_ref() {
format!("a file ending with `.{}` in `{}`", ext, path)
} else {
format!("a file in `{}`", path)
}
},
ChangelogStyle::File { ref path } => {
format!("the `{}` file", path)
},
}
}
fn applies(&self, diff_path: &Path) -> bool {
match *self {
ChangelogStyle::Directory { ref path, ref extension } => {
let ext_ok = extension.as_ref()
.map_or(true, |ext| {
diff_path.extension()
.map_or(false, |diff_ext| diff_ext == (ext.as_ref() as &Path))
});
ext_ok && diff_path.starts_with(path)
},
ChangelogStyle::File { ref path } => {
diff_path == (path.as_ref() as &Path)
},
}
}
fn is_ok(&self, status: StatusChange) -> bool {
match *self {
ChangelogStyle::Directory { .. } => {
match status {
StatusChange::Added |
StatusChange::Modified(_) |
StatusChange::Deleted => true,
_ => false,
}
},
ChangelogStyle::File { .. } => {
match status {
StatusChange::Added |
StatusChange::Modified(_) => true,
_ => false,
}
},
}
}
}
#[derive(Debug)]
pub struct Changelog {
style: ChangelogStyle,
required: bool,
}
impl Changelog {
pub fn new(style: ChangelogStyle) -> Self {
Self {
style: style,
required: false,
}
}
pub fn required(&mut self, required: bool) -> &mut Self {
self.required = required;
self
}
}
impl ContentCheck for Changelog {
fn name(&self) -> &str {
"changelog"
}
fn check(&self, _: &CheckGitContext, content: &Content) -> Result<CheckResult> {
let mut result = CheckResult::new();
let changelog_changes = content.diffs()
.par_iter()
.filter(|diff| {
self.style.applies(diff.name.as_path()) &&
self.style.is_ok(diff.status)
})
.count();
if changelog_changes == 0 {
if self.required {
result.add_error(format!("{}missing a changelog entry in {}.",
commit_prefix_str(content, "not allowed;"),
self.style.describe()));
} else {
result.add_warning(format!("{}please consider adding a changelog entry in {}.",
commit_prefix_str(content, "is missing a changelog entry;"),
self.style.describe()));
};
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use checks::{Changelog, ChangelogStyle};
use checks::test::*;
const CHANGELOG_DELETE: &str = "e86c0859ed36311c2ebce1ff50790eb21eabba78";
const CHANGELOG_MISSING: &str = "66953d52f3ec6f6e4d731e7f2f70dc4000ab13ae";
const CHANGELOG_MISSING_FIXED: &str = "a1020529e12fab5f1f7c87c60247d0068e0c9d8c";
const CHANGELOG_MISSING_FIXED_BAD_EXT: &str = "72c4a5ead2fcb5ce6017a391a1767294944c3e9c";
const FILE_CHANGELOG_INIT: &str = "3cd51c974845ff0c120e87a8e20ad5cf44798321";
const FILE_CHANGELOG_ADDED: &str = "34762d3ec96e2a302a30842ccbb5765c2b4a61d5";
const DIRECTORY_CHANGELOG_ADD: &str = "ff67b91112f4af4861528ac11b1797490ce18fc4";
const DIRECTORY_CHANGELOG_DELETE: &str = "114c724c1def28ecc96f10a8dab462879c80580a";
const DIRECTORY_CHANGELOG_MODIFY: &str = "f2719062d6c9e7c3835b397bd9553fb7b68cce5f";
const DIRECTORY_CHANGELOG_PREFIX: &str = "5f5442e33b6d0dfe01a14d98476d14e54c4d590e";
const DIRECTORY_CHANGELOG_BAD_EXT: &str = "93e235f10a76d58581f2d6056faa9d796156c3ea";
fn file_changelog() -> Changelog {
let mut check = Changelog::new(ChangelogStyle::file("changelog.md"));
check.required(true);
check
}
fn directory_changelog() -> Changelog {
let mut check = Changelog::new(ChangelogStyle::directory("changes", None));
check.required(true);
check
}
fn directory_changelog_ext() -> Changelog {
let mut check = Changelog::new(ChangelogStyle::directory("changes", Some("md".to_string())));
check.required(true);
check
}
#[test]
fn test_changelog_file() {
let check = file_changelog();
let result = run_check("test_changelog_file", CHANGELOG_MISSING, check);
test_result_errors(result, &[
"commit 66953d52f3ec6f6e4d731e7f2f70dc4000ab13ae not allowed; missing a changelog \
entry in the `changelog.md` file.",
]);
}
#[test]
fn test_changelog_file_init() {
let check = file_changelog();
run_check_ok("test_changelog_file_init", FILE_CHANGELOG_INIT, check);
}
#[test]
fn test_changelog_file_ok() {
let check = file_changelog();
run_check_ok("test_changelog_file_ok", FILE_CHANGELOG_ADDED, check);
}
#[test]
fn test_changelog_file_delete() {
let check = file_changelog();
let result = run_check("test_changelog_file_delete", CHANGELOG_DELETE, check);
test_result_errors(result, &[
"commit e86c0859ed36311c2ebce1ff50790eb21eabba78 not allowed; missing a changelog \
entry in the `changelog.md` file.",
]);
}
#[test]
fn test_changelog_directory() {
let check = directory_changelog();
let result = run_check("test_changelog_directory", CHANGELOG_MISSING, check);
test_result_errors(result, &[
"commit 66953d52f3ec6f6e4d731e7f2f70dc4000ab13ae not allowed; missing a changelog \
entry in a file in `changes`.",
]);
}
#[test]
fn test_changelog_directory_bad_extension() {
let check = directory_changelog_ext();
let result = run_check("test_changelog_directory_bad_extension", DIRECTORY_CHANGELOG_BAD_EXT, check);
test_result_errors(result, &[
"commit 93e235f10a76d58581f2d6056faa9d796156c3ea not allowed; missing a changelog \
entry in a file ending with `.md` in `changes`.",
]);
}
#[test]
fn test_changelog_directory_delete() {
let check = directory_changelog();
let conf = make_check_conf(&check);
let result = test_check_base("test_changelog_directory_delete",
DIRECTORY_CHANGELOG_DELETE,
CHANGELOG_MISSING_FIXED_BAD_EXT,
&conf);
test_result_ok(result);
}
#[test]
fn test_changelog_directory_modify() {
let check = directory_changelog();
let conf = make_check_conf(&check);
let result = test_check_base("test_changelog_directory_modify",
DIRECTORY_CHANGELOG_MODIFY,
CHANGELOG_MISSING_FIXED_BAD_EXT,
&conf);
test_result_ok(result);
}
#[test]
fn test_changelog_directory_prefix() {
let check = directory_changelog();
let result = run_check("test_changelog_directory_prefix", DIRECTORY_CHANGELOG_PREFIX, check);
test_result_errors(result, &[
"commit 5f5442e33b6d0dfe01a14d98476d14e54c4d590e not allowed; missing a changelog \
entry in a file in `changes`.",
]);
}
#[test]
fn test_changelog_directory_ok() {
let check = directory_changelog();
run_check_ok("test_changelog_directory_ok", DIRECTORY_CHANGELOG_ADD, check);
}
#[test]
fn test_changelog_directory_ok_extension() {
let check = directory_changelog_ext();
run_check_ok("test_changelog_directory_ok_extension", DIRECTORY_CHANGELOG_ADD, check);
}
#[test]
fn test_changelog_warning_file() {
let mut check = file_changelog();
check.required(false);
let result = run_check("test_changelog_warning_directory", CHANGELOG_MISSING, check);
test_result_warnings(result, &[
"commit 66953d52f3ec6f6e4d731e7f2f70dc4000ab13ae is missing a changelog entry; please \
consider adding a changelog entry in the `changelog.md` file.",
]);
}
#[test]
fn test_changelog_warning_directory() {
let mut check = directory_changelog();
check.required(false);
let result = run_check("test_changelog_warning_directory", CHANGELOG_MISSING, check);
test_result_warnings(result, &[
"commit 66953d52f3ec6f6e4d731e7f2f70dc4000ab13ae is missing a changelog entry; please \
consider adding a changelog entry in a file in `changes`.",
]);
}
#[test]
fn test_changelog_topic_file() {
let check = file_changelog();
let result = run_topic_check("test_changelog_topic_file", CHANGELOG_MISSING, check);
test_result_errors(result, &[
"missing a changelog entry in the `changelog.md` file.",
]);
}
#[test]
fn test_changelog_topic_file_warning() {
let mut check = directory_changelog();
check.required(false);
let result = run_topic_check("test_changelog_topic_file_warning", CHANGELOG_MISSING, check);
test_result_warnings(result, &[
"please consider adding a changelog entry in a file in `changes`.",
]);
}
#[test]
fn test_changelog_topic_directory() {
let check = directory_changelog();
let result = run_topic_check("test_changelog_topic_directory", CHANGELOG_MISSING, check);
test_result_errors(result, &[
"missing a changelog entry in a file in `changes`.",
]);
}
#[test]
fn test_changelog_topic_directory_warning() {
let mut check = directory_changelog();
check.required(false);
let result = run_topic_check("test_changelog_topic_directory_warning", CHANGELOG_MISSING, check);
test_result_warnings(result, &[
"please consider adding a changelog entry in a file in `changes`.",
]);
}
#[test]
fn test_changelog_topic_directory_bad_ext() {
let check = directory_changelog_ext();
let result = run_topic_check("test_changelog_topic_directory_bad_ext", CHANGELOG_MISSING_FIXED, check);
test_result_errors(result, &[
"missing a changelog entry in a file ending with `.md` in `changes`.",
]);
}
#[test]
fn test_changelog_topic_directory_warning_bad_ext() {
let mut check = directory_changelog_ext();
check.required(false);
let result = run_topic_check("test_changelog_topic_directory_warning_bad_ext", CHANGELOG_MISSING_FIXED, check);
test_result_warnings(result, &[
"please consider adding a changelog entry in a file ending with `.md` in `changes`.",
]);
}
#[test]
fn test_changelog_topic_fixed_file() {
let check = directory_changelog();
run_topic_check_ok("test_changelog_topic_fixed_file", CHANGELOG_MISSING_FIXED, check);
}
#[test]
fn test_changelog_topic_fixed_directory() {
let check = directory_changelog();
run_topic_check_ok("test_changelog_topic_fixed_directory", CHANGELOG_MISSING_FIXED, check);
}
#[test]
fn test_changelog_topic_fixed_directory_bad_ext() {
let check = directory_changelog();
run_topic_check_ok("test_changelog_topic_fixed_directory_bad_ext", CHANGELOG_MISSING_FIXED_BAD_EXT, check);
}
}