tiger_lib/parse/
ignore.rs

1use std::sync::LazyLock;
2
3use lalrpop_util::lalrpop_mod;
4
5use crate::report::ErrorKey;
6
7lalrpop_mod! {
8    #[allow(clippy::pedantic)]
9    #[allow(clippy::if_then_some_else_none)]
10    parser, "/parse/ignore/parser.rs"
11}
12static COMMENT_PARSER: LazyLock<parser::CommentParser> = LazyLock::new(parser::CommentParser::new);
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
15pub enum IgnoreSize {
16    #[default]
17    Line,
18    Block,
19    File,
20    Begin,
21    End,
22}
23
24#[derive(Debug, Clone, Default)]
25pub struct IgnoreFilter {
26    key: Option<ErrorKey>,
27    text: Option<String>,
28}
29
30#[derive(Debug, Clone, Default)]
31pub struct IgnoreSpec {
32    pub size: IgnoreSize,
33    pub filter: IgnoreFilter,
34}
35
36pub fn parse_comment(comment: &str) -> Option<IgnoreSpec> {
37    // Shortcut for performance
38    if !comment.trim_start().starts_with("tiger-ignore") {
39        return None;
40    }
41    COMMENT_PARSER.parse(comment).ok()
42}
43
44impl IgnoreSpec {
45    fn set_key(mut self, key: ErrorKey) -> Self {
46        self.filter.key = Some(key);
47        self
48    }
49
50    fn set_text(mut self, text: String) -> Self {
51        self.filter.text = Some(text);
52        self
53    }
54
55    fn merge(mut self, other: Self) -> Self {
56        if other.size != IgnoreSize::Line {
57            self.size = other.size;
58        }
59        if other.filter.key.is_some() {
60            self.filter.key = other.filter.key;
61        }
62        if other.filter.text.is_some() {
63            self.filter.text = other.filter.text;
64        }
65        self
66    }
67}
68
69impl IgnoreFilter {
70    pub fn matches(&self, other_key: ErrorKey, other_text: &str) -> bool {
71        if let Some(key) = self.key {
72            if key != other_key {
73                return false;
74            }
75        }
76        if let Some(text) = &self.text {
77            if !other_text.contains(text) {
78                return false;
79            }
80        }
81        true
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_bare() {
91        let result = parse_comment("tiger-ignore");
92        assert!(result.is_some());
93        if let Some(spec) = result {
94            assert_eq!(spec.size, IgnoreSize::Line);
95            assert!(spec.filter.key.is_none());
96            assert!(spec.filter.text.is_none());
97        }
98    }
99
100    #[test]
101    fn test_non_ignore() {
102        let result = parse_comment("a random comment");
103        assert!(result.is_none());
104    }
105
106    #[test]
107    fn test_bare_trailing() {
108        let result = parse_comment("tiger-ignore with explanation");
109        assert!(result.is_some());
110        if let Some(spec) = result {
111            assert_eq!(spec.size, IgnoreSize::Line);
112            assert!(spec.filter.key.is_none());
113            assert!(spec.filter.text.is_none());
114        }
115    }
116
117    #[test]
118    fn test_block() {
119        let result = parse_comment("tiger-ignore(block)");
120        assert!(result.is_some());
121        if let Some(spec) = result {
122            assert_eq!(spec.size, IgnoreSize::Block);
123            assert!(spec.filter.key.is_none());
124            assert!(spec.filter.text.is_none());
125        }
126    }
127
128    #[test]
129    fn test_file() {
130        let result = parse_comment("tiger-ignore(file)");
131        assert!(result.is_some());
132        if let Some(spec) = result {
133            assert_eq!(spec.size, IgnoreSize::File);
134            assert!(spec.filter.key.is_none());
135            assert!(spec.filter.text.is_none());
136        }
137    }
138
139    #[test]
140    fn test_begin() {
141        let result = parse_comment("tiger-ignore(begin)");
142        assert!(result.is_some());
143        if let Some(spec) = result {
144            assert_eq!(spec.size, IgnoreSize::Begin);
145            assert!(spec.filter.key.is_none());
146            assert!(spec.filter.text.is_none());
147        }
148    }
149
150    #[test]
151    fn test_end() {
152        let result = parse_comment("tiger-ignore(end)");
153        assert!(result.is_some());
154        if let Some(spec) = result {
155            assert_eq!(spec.size, IgnoreSize::End);
156            assert!(spec.filter.key.is_none());
157            assert!(spec.filter.text.is_none());
158        }
159    }
160
161    #[test]
162    fn test_block_with_key() {
163        let result = parse_comment("tiger-ignore(block, key=missing-item)");
164        assert!(result.is_some());
165        if let Some(spec) = result {
166            assert_eq!(spec.size, IgnoreSize::Block);
167            assert_eq!(spec.filter.key, Some(ErrorKey::MissingItem));
168            assert!(spec.filter.text.is_none());
169        }
170    }
171
172    #[test]
173    fn test_block_with_quoted_text() {
174        let result = parse_comment("tiger-ignore(block, text=\"missing english\")");
175        assert!(result.is_some());
176        if let Some(spec) = result {
177            assert_eq!(spec.size, IgnoreSize::Block);
178            assert!(spec.filter.key.is_none());
179            assert_eq!(spec.filter.text, Some("missing english".to_owned()));
180        }
181    }
182}