darklua_core/rules/
empty_do.rs1use crate::nodes::{Block, Statement};
2use crate::process::{DefaultVisitor, NodeProcessor, NodeVisitor};
3use crate::rules::{
4 Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, 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
38impl FlawlessRule for RemoveEmptyDo {
39 fn flawless_process(&self, block: &mut Block, _: &Context) {
40 loop {
41 let mut processor = EmptyDoFilter::default();
42 DefaultVisitor::visit_block(block, &mut processor);
43 if !processor.has_mutated() {
44 break;
45 }
46 }
47 }
48}
49
50impl RuleConfiguration for RemoveEmptyDo {
51 fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
52 verify_no_rule_properties(&properties)?;
53
54 Ok(())
55 }
56
57 fn get_name(&self) -> &'static str {
58 REMOVE_EMPTY_DO_RULE_NAME
59 }
60
61 fn serialize_to_properties(&self) -> RuleProperties {
62 RuleProperties::new()
63 }
64}
65
66#[cfg(test)]
67mod test {
68 use super::*;
69 use crate::nodes::DoStatement;
70 use crate::rules::{ContextBuilder, Rule};
71 use crate::Resources;
72
73 use insta::assert_json_snapshot;
74
75 fn new_rule() -> RemoveEmptyDo {
76 RemoveEmptyDo::default()
77 }
78
79 #[test]
80 fn remove_empty_do_statement() {
81 let rule = new_rule();
82
83 let mut block = Block::default().with_statement(DoStatement::new(Block::default()));
84
85 rule.process(
86 &mut block,
87 &ContextBuilder::new(".", &Resources::from_memory(), "").build(),
88 )
89 .expect("rule should succeed");
90
91 assert_eq!(block, Block::default());
92 }
93
94 #[test]
95 fn remove_nested_empty_do_statement() {
96 let rule = new_rule();
97
98 let block_with_do_statement = Block::default().with_statement(DoStatement::default());
99 let mut block = Block::default().with_statement(DoStatement::new(block_with_do_statement));
100
101 rule.process(
102 &mut block,
103 &ContextBuilder::new(".", &Resources::from_memory(), "").build(),
104 )
105 .expect("rule should succeed");
106
107 assert_eq!(block, Block::default());
108 }
109
110 #[test]
111 fn serialize_default_rule() {
112 let rule: Box<dyn Rule> = Box::new(new_rule());
113
114 assert_json_snapshot!("default_remove_empty_do", rule);
115 }
116
117 #[test]
118 fn configure_with_extra_field_error() {
119 let result = json5::from_str::<Box<dyn Rule>>(
120 r#"{
121 rule: 'remove_empty_do',
122 prop: "something",
123 }"#,
124 );
125 pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
126 }
127}