use regex::{Captures, Regex};
use std::{collections::HashMap, sync::LazyLock};
static NEXT_LINE_RE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"//\s*natlint-disable-next-line(?:\s+([\w\s,]+))?").unwrap());
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DisableDirectives {
rules_by_line: HashMap<usize, Option<Vec<String>>>,
}
impl DisableDirectives {
const fn new(rules_by_line: HashMap<usize, Option<Vec<String>>>) -> Self {
Self { rules_by_line }
}
pub fn is_disabled(&self, line: usize, rule_name: &str) -> bool {
self.rules_by_line.get(&line).is_some_and(|rules| {
rules
.as_ref()
.is_none_or(|disabled_rules| disabled_rules.contains(&rule_name.to_owned()))
})
}
}
pub fn disable_next_line_directives(content: &str) -> DisableDirectives {
DisableDirectives::new(
content
.lines()
.enumerate()
.filter_map(|(idx, line)| {
NEXT_LINE_RE.captures(line).map(|caps| {
(
idx + 2, rules_from_captures(&caps),
)
})
})
.collect(),
)
}
fn rules_from_captures(captures: &Captures) -> Option<Vec<String>> {
let rules: Vec<String> = captures
.get(1)?
.as_str()
.split(',')
.map(|s| s.trim().to_owned())
.filter(|s| !s.is_empty())
.collect();
(!rules.is_empty()).then_some(rules)
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_disable_next_line {
($name:ident, $source:expr, $expected:expr) => {
#[test]
fn $name() {
assert_eq!(
disable_next_line_directives($source),
DisableDirectives::new($expected)
);
}
};
}
test_disable_next_line!(
no_directives,
r"
uint a;
function foo() {}
",
HashMap::new()
);
test_disable_next_line!(
disables_all_rules_on_next_line,
r"
// natlint-disable-next-line
uint a;
",
HashMap::from([(3, None)])
);
test_disable_next_line!(
disables_single_rule,
r"
// natlint-disable-next-line MissingNotice
uint a;
",
HashMap::from([(3, Some(vec!["MissingNotice".to_owned()]))])
);
test_disable_next_line!(
disables_multiple_rules_with_commas,
r"
// natlint-disable-next-line MissingNotice,MissingParam,AnotherRule
uint a;
",
HashMap::from([(
3,
Some(vec![
"MissingNotice".to_owned(),
"MissingParam".to_owned(),
"AnotherRule".to_owned()
])
)])
);
test_disable_next_line!(
disables_multiple_rules_with_spaces,
r"
// natlint-disable-next-line MissingNotice, MissingParam, AnotherRule
uint a;
",
HashMap::from([(
3,
Some(vec![
"MissingNotice".to_owned(),
"MissingParam".to_owned(),
"AnotherRule".to_owned()
])
)])
);
test_disable_next_line!(
multiple_directives_in_one_file,
r"
// natlint-disable-next-line
uint a;
// natlint-disable-next-line MissingNotice,MissingParam
function foo() {}
",
HashMap::from([
(3, None),
(
6,
Some(vec!["MissingNotice".to_owned(), "MissingParam".to_owned()])
)
])
);
}