use derive_builder::Builder;
use git_checks_core::impl_prelude::*;
use std::char::REPLACEMENT_CHARACTER;
#[derive(Builder, Debug, Default, Clone, Copy)]
#[builder(field(private))]
pub struct InvalidUtf8 {}
impl InvalidUtf8 {
pub fn builder() -> InvalidUtf8Builder {
InvalidUtf8Builder::default()
}
}
impl ContentCheck for InvalidUtf8 {
fn name(&self) -> &str {
"invalid-utf8"
}
fn check(
&self,
ctx: &CheckGitContext,
content: &dyn Content,
) -> Result<CheckResult, Box<dyn Error>> {
let mut result = CheckResult::new();
for diff in content.diffs() {
match diff.status {
StatusChange::Added | StatusChange::Modified(_) => (),
_ => continue,
}
let diff_attr = ctx.check_attr("diff", diff.name.as_path())?;
if let AttributeState::Unset = diff_attr {
continue;
}
let patch = match content.path_diff(&diff.name) {
Ok(s) => s,
Err(err) => {
result.add_alert(
format!(
"{}failed to get the diff for file `{}`: {}.",
commit_prefix(content),
diff.name,
err,
),
true,
);
continue;
},
};
for line in patch.lines().filter(|line| line.starts_with('+')) {
if line.contains(REPLACEMENT_CHARACTER) {
let safe_line = line[1..].replace('\\', "\\\\").replace('`', "\\`");
result.add_error(format!(
"{}invalid utf-8 sequence added in `{}`: `{}`.",
commit_prefix_str(content, "not allowed;"),
diff.name,
safe_line,
));
}
}
}
Ok(result)
}
}
#[cfg(feature = "config")]
pub(crate) mod config {
use git_checks_config::{register_checks, CommitCheckConfig, IntoCheck, TopicCheckConfig};
use serde::Deserialize;
#[cfg(test)]
use serde_json::json;
use crate::InvalidUtf8;
#[derive(Deserialize, Debug)]
pub struct InvalidUtf8Config {}
impl IntoCheck for InvalidUtf8Config {
type Check = InvalidUtf8;
fn into_check(self) -> Self::Check {
InvalidUtf8::default()
}
}
register_checks! {
InvalidUtf8Config {
"invalid_utf8" => CommitCheckConfig,
"invalid_utf8/topic" => TopicCheckConfig,
},
}
#[test]
fn test_invalid_utf8_config_empty() {
let json = json!({});
let check: InvalidUtf8Config = serde_json::from_value(json).unwrap();
let _ = check.into_check();
}
}
#[cfg(test)]
mod tests {
use git_checks_core::{Check, TopicCheck};
use crate::test::*;
use crate::InvalidUtf8;
const BAD_TOPIC: &str = "cf16b71a21023320ffab7b3f7673dc62f33e5022";
const DELETE_TOPIC: &str = "0f5b216e14e1af3e8e8b5bbb8116c3a89a35f73f";
const FIX_TOPIC: &str = "e8763477e9ebef4a61d130724cee9e29b13f857e";
#[test]
fn test_invalid_utf8_builder_default() {
assert!(InvalidUtf8::builder().build().is_ok());
}
#[test]
fn test_invalid_utf8_name_commit() {
let check = InvalidUtf8::default();
assert_eq!(Check::name(&check), "invalid-utf8");
}
#[test]
fn test_invalid_utf8_name_topic() {
let check = InvalidUtf8::default();
assert_eq!(TopicCheck::name(&check), "invalid-utf8");
}
#[test]
fn test_invalid_utf8() {
let check = InvalidUtf8::default();
let result = run_check("test_invalid_utf8", BAD_TOPIC, check);
test_result_errors(result, &[
"commit cf16b71a21023320ffab7b3f7673dc62f33e5022 not allowed; invalid utf-8 sequence \
added in `invalid-utf8`: `This file contains an invalid utf-8 sequence: \u{fffd}`.",
]);
}
#[test]
fn test_invalid_utf8_topic() {
let check = InvalidUtf8::default();
let result = run_topic_check("test_invalid_utf8_topic", BAD_TOPIC, check);
test_result_errors(result, &[
"invalid utf-8 sequence added in `invalid-utf8`: `This file contains an invalid utf-8 \
sequence: \u{fffd}`.",
]);
}
#[test]
fn test_invalid_utf8_delete_file() {
let check = InvalidUtf8::default();
let conf = make_check_conf(&check);
let result = test_check_base(
"test_invalid_utf8_delete_file",
DELETE_TOPIC,
BAD_TOPIC,
&conf,
);
test_result_ok(result);
}
#[test]
fn test_invalid_utf8_delete_file_topic() {
let check = InvalidUtf8::default();
let result = run_topic_check("test_invalid_utf8_delete_file_topic", DELETE_TOPIC, check);
test_result_ok(result);
}
#[test]
fn test_invalid_utf8_topic_fixed() {
let check = InvalidUtf8::default();
run_topic_check_ok("test_invalid_utf8_topic_fixed", FIX_TOPIC, check);
}
}