use crate::checker::Checker;
use crate::diagnostic::{Diagnostic, Severity};
use crate::po::entry::Entry;
use crate::po::message::Message;
use crate::rules::rule::RuleChecker;
pub struct WhitespaceStartRule;
impl RuleChecker for WhitespaceStartRule {
fn name(&self) -> &'static str {
"whitespace-start"
}
fn is_default(&self) -> bool {
true
}
fn is_check(&self) -> bool {
true
}
fn severity(&self) -> Severity {
Severity::Info
}
fn check_msg(
&self,
checker: &Checker,
_entry: &Entry,
msgid: &Message,
msgstr: &Message,
) -> Vec<Diagnostic> {
if msgid.value.trim().is_empty() || msgstr.value.trim().is_empty() {
return vec![];
}
let id_ws = get_whitespace_start(&msgid.value);
let str_ws = get_whitespace_start(&msgstr.value);
if id_ws == str_ws {
vec![]
} else {
vec![
self.new_diag(
checker,
format!("inconsistent leading whitespace ('{id_ws}' / '{str_ws}')"),
)
.with_msgs_hl(
msgid,
&[(0, id_ws.len())],
msgstr,
&[(0, str_ws.len())],
),
]
}
}
}
pub struct WhitespaceEndRule;
impl RuleChecker for WhitespaceEndRule {
fn name(&self) -> &'static str {
"whitespace-end"
}
fn is_default(&self) -> bool {
true
}
fn is_check(&self) -> bool {
true
}
fn severity(&self) -> Severity {
Severity::Info
}
fn check_msg(
&self,
checker: &Checker,
_entry: &Entry,
msgid: &Message,
msgstr: &Message,
) -> Vec<Diagnostic> {
if msgid.value.trim().is_empty() || msgstr.value.trim().is_empty() {
return vec![];
}
let id_ws = get_whitespace_end(&msgid.value);
let str_ws = get_whitespace_end(&msgstr.value);
if id_ws == str_ws {
vec![]
} else {
vec![
self.new_diag(
checker,
format!("inconsistent trailing whitespace ('{id_ws}' / '{str_ws}')"),
)
.with_msgs_hl(
msgid,
&[(msgid.value.len() - id_ws.len(), msgid.value.len())],
msgstr,
&[(msgstr.value.len() - str_ws.len(), msgstr.value.len())],
),
]
}
}
}
fn get_whitespace_start(value: &str) -> &str {
let pos = value
.chars()
.take_while(|c| c.is_whitespace() && *c != '\n')
.map(char::len_utf8)
.sum::<usize>();
&value[..pos]
}
fn get_whitespace_end(value: &str) -> &str {
let pos = value
.chars()
.rev()
.take_while(|c| c.is_whitespace() && *c != '\n')
.map(char::len_utf8)
.sum::<usize>();
&value[value.len() - pos..]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{diagnostic::Diagnostic, rules::rule::Rules};
fn check_whitespace_start(content: &str) -> Vec<Diagnostic> {
let mut checker = Checker::new(content.as_bytes());
let rules = Rules::new(vec![Box::new(WhitespaceStartRule {})]);
checker.do_all_checks(&rules);
checker.diagnostics
}
fn check_whitespace_end(content: &str) -> Vec<Diagnostic> {
let mut checker = Checker::new(content.as_bytes());
let rules = Rules::new(vec![Box::new(WhitespaceEndRule {})]);
checker.do_all_checks(&rules);
checker.diagnostics
}
#[test]
fn test_get_whitespace_start() {
assert_eq!(get_whitespace_start(""), "");
assert_eq!(get_whitespace_start("test"), "");
assert_eq!(get_whitespace_start(" test"), " ");
assert_eq!(get_whitespace_start("\ttest"), "\t");
assert_eq!(get_whitespace_start(" \ttest"), " \t");
assert_eq!(get_whitespace_start("\n test"), "");
}
#[test]
fn test_get_whitespace_end() {
assert_eq!(get_whitespace_end(""), "");
assert_eq!(get_whitespace_end("test"), "");
assert_eq!(get_whitespace_end("test "), " ");
assert_eq!(get_whitespace_end("test\t"), "\t");
assert_eq!(get_whitespace_end("test\t "), "\t ");
assert_eq!(get_whitespace_end("test \n"), "");
}
#[test]
fn test_no_whitespace() {
let diags = check_whitespace_start(
r#"
msgid "tested"
msgstr "testé"
"#,
);
assert!(diags.is_empty());
let diags = check_whitespace_end(
r#"
msgid "tested"
msgstr "testé"
"#,
);
assert!(diags.is_empty());
}
#[test]
fn test_whitespace_ok() {
let diags = check_whitespace_start(
r#"
msgid " tested "
msgstr " testé "
"#,
);
assert!(diags.is_empty());
let diags = check_whitespace_end(
r#"
msgid " tested "
msgstr " testé "
"#,
);
assert!(diags.is_empty());
}
#[test]
fn test_whitespace_error_noqa() {
let diags = check_whitespace_start(
r#"
#, noqa:whitespace-start
msgid " tested "
msgstr "testé "
"#,
);
assert!(diags.is_empty());
let diags = check_whitespace_end(
r#"
#, noqa:whitespace-end
msgid " tested "
msgstr "testé "
"#,
);
assert!(diags.is_empty());
}
#[test]
fn test_whitespace_error() {
let diags = check_whitespace_start(
r#"
msgid " tested "
msgstr "testé "
"#,
);
assert_eq!(diags.len(), 1);
let diag = &diags[0];
assert_eq!(diag.severity, Severity::Info);
assert_eq!(diag.message, "inconsistent leading whitespace (' ' / '')");
let diags = check_whitespace_end(
r#"
msgid " tested "
msgstr "testé "
"#,
);
assert_eq!(diags.len(), 1);
let diag = &diags[0];
assert_eq!(diag.severity, Severity::Info);
assert_eq!(
diag.message,
"inconsistent trailing whitespace (' ' / ' ')"
);
}
}