Skip to main content

darklua_core/rules/
empty_do.rs

1use crate::nodes::{Block, Statement};
2use crate::process::{DefaultVisitor, NodeProcessor, NodeVisitor};
3use crate::rules::{
4    Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleMetadata, RuleProperties,
5};
6
7use super::verify_no_rule_properties;
8
9#[derive(Debug, Default)]
10struct EmptyDoFilter {
11    mutated: bool,
12}
13
14impl EmptyDoFilter {
15    pub fn has_mutated(&self) -> bool {
16        self.mutated
17    }
18}
19
20impl NodeProcessor for EmptyDoFilter {
21    fn process_block(&mut self, block: &mut Block) {
22        block.filter_statements(|statement| match statement {
23            Statement::Do(do_statement) => {
24                self.mutated = do_statement.get_block().is_empty();
25                !self.mutated
26            }
27            _ => true,
28        });
29    }
30}
31
32pub const REMOVE_EMPTY_DO_RULE_NAME: &str = "remove_empty_do";
33
34/// A rule that removes empty do statements.
35#[derive(Debug, Default, PartialEq, Eq)]
36pub struct RemoveEmptyDo {
37    metadata: RuleMetadata,
38}
39
40impl FlawlessRule for RemoveEmptyDo {
41    fn flawless_process(&self, block: &mut Block, _: &Context) {
42        loop {
43            let mut processor = EmptyDoFilter::default();
44            DefaultVisitor::visit_block(block, &mut processor);
45            if !processor.has_mutated() {
46                break;
47            }
48        }
49    }
50}
51
52impl RuleConfiguration for RemoveEmptyDo {
53    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
54        verify_no_rule_properties(&properties)?;
55
56        Ok(())
57    }
58
59    fn get_name(&self) -> &'static str {
60        REMOVE_EMPTY_DO_RULE_NAME
61    }
62
63    fn serialize_to_properties(&self) -> RuleProperties {
64        RuleProperties::new()
65    }
66
67    fn set_metadata(&mut self, metadata: RuleMetadata) {
68        self.metadata = metadata;
69    }
70
71    fn metadata(&self) -> &RuleMetadata {
72        &self.metadata
73    }
74}
75
76#[cfg(test)]
77mod test {
78    use super::*;
79    use crate::nodes::DoStatement;
80    use crate::rules::{ContextBuilder, Rule};
81    use crate::Resources;
82
83    use insta::assert_json_snapshot;
84
85    fn new_rule() -> RemoveEmptyDo {
86        RemoveEmptyDo::default()
87    }
88
89    #[test]
90    fn remove_empty_do_statement() {
91        let rule = new_rule();
92
93        let mut block = Block::default().with_statement(DoStatement::new(Block::default()));
94
95        rule.process(
96            &mut block,
97            &ContextBuilder::new(".", &Resources::from_memory(), "").build(),
98        )
99        .expect("rule should succeed");
100
101        assert_eq!(block, Block::default());
102    }
103
104    #[test]
105    fn remove_nested_empty_do_statement() {
106        let rule = new_rule();
107
108        let block_with_do_statement = Block::default().with_statement(DoStatement::default());
109        let mut block = Block::default().with_statement(DoStatement::new(block_with_do_statement));
110
111        rule.process(
112            &mut block,
113            &ContextBuilder::new(".", &Resources::from_memory(), "").build(),
114        )
115        .expect("rule should succeed");
116
117        assert_eq!(block, Block::default());
118    }
119
120    #[test]
121    fn serialize_default_rule() {
122        let rule: Box<dyn Rule> = Box::new(new_rule());
123
124        assert_json_snapshot!(rule, @r###""remove_empty_do""###);
125    }
126
127    #[test]
128    fn configure_with_extra_field_error() {
129        let result = json5::from_str::<Box<dyn Rule>>(
130            r#"{
131            rule: 'remove_empty_do',
132            prop: "something",
133        }"#,
134        );
135        insta::assert_snapshot!(result.unwrap_err().to_string(), @"unexpected field 'prop' at line 1 column 1");
136    }
137}