darklua_core/rules/
empty_do.rs1use 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#[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}