darklua_core/rules/
group_local.rs

1use crate::nodes::{Block, Expression, LocalAssignStatement, Statement};
2use crate::process::processors::FindVariables;
3use crate::process::{DefaultVisitor, NodeProcessor, NodeVisitor};
4use crate::rules::{
5    Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties,
6};
7
8use std::iter;
9
10use super::verify_no_rule_properties;
11
12#[derive(Debug, Clone, Default)]
13struct GroupLocalProcessor {}
14
15impl GroupLocalProcessor {
16    fn filter_statements(&self, block: &mut Block) -> Vec<Statement> {
17        let mut statements = block.take_statements();
18        let mut filter_statements = Vec::new();
19        let mut iter = statements.drain(..);
20        let mut previous_statement = iter.next();
21        let mut current_statement = iter.next();
22
23        while let Some(current) = current_statement {
24            previous_statement = if let Some(previous) = previous_statement {
25                use Statement::LocalAssign;
26
27                match (previous, current) {
28                    (LocalAssign(mut previous), LocalAssign(mut current)) => {
29                        if self.should_merge(&previous, &mut current) {
30                            self.merge(&mut previous, current);
31
32                            Some(LocalAssign(previous))
33                        } else {
34                            filter_statements.push(LocalAssign(previous));
35                            Some(LocalAssign(current))
36                        }
37                    }
38                    (previous, current) => {
39                        filter_statements.push(previous);
40                        Some(current)
41                    }
42                }
43            } else {
44                None
45            };
46
47            current_statement = iter.next();
48        }
49
50        if let Some(previous) = previous_statement {
51            filter_statements.push(previous);
52        }
53
54        filter_statements
55    }
56
57    fn should_merge(&self, first: &LocalAssignStatement, next: &mut LocalAssignStatement) -> bool {
58        let first_value_count = first.values_len();
59
60        if first.variables_len() > first_value_count && first_value_count != 0 {
61            return false;
62        }
63
64        let mut find_variables: FindVariables = first
65            .iter_variables()
66            .map(|variable| variable.get_name().as_str())
67            .collect();
68
69        next.iter_mut_values().all(|expression| {
70            DefaultVisitor::visit_expression(expression, &mut find_variables);
71            !find_variables.has_found_usage()
72        })
73    }
74
75    fn merge(&self, first: &mut LocalAssignStatement, mut other: LocalAssignStatement) {
76        if first.values_len() == 0 && other.values_len() != 0 {
77            let variable_count = first.variables_len();
78            first.extend_values(iter::repeat(Expression::nil()).take(variable_count));
79        }
80
81        if other.values_len() == 0 && first.values_len() != 0 {
82            let variable_count = other.variables_len();
83            other.extend_values(iter::repeat(Expression::nil()).take(variable_count));
84        }
85
86        let (mut variables, mut values) = other.into_assignments();
87        first.append_variables(&mut variables);
88        first.append_values(&mut values);
89    }
90}
91
92impl NodeProcessor for GroupLocalProcessor {
93    fn process_block(&mut self, block: &mut Block) {
94        let filter_statements = self.filter_statements(block);
95
96        block.set_statements(filter_statements);
97    }
98}
99
100pub const GROUP_LOCAL_ASSIGNMENT_RULE_NAME: &str = "group_local_assignment";
101
102/// Group local assign statements into one statement.
103#[derive(Debug, Default, PartialEq, Eq)]
104pub struct GroupLocalAssignment {}
105
106impl FlawlessRule for GroupLocalAssignment {
107    fn flawless_process(&self, block: &mut Block, _: &Context) {
108        let mut processor = GroupLocalProcessor::default();
109        DefaultVisitor::visit_block(block, &mut processor);
110    }
111}
112
113impl RuleConfiguration for GroupLocalAssignment {
114    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
115        verify_no_rule_properties(&properties)?;
116
117        Ok(())
118    }
119
120    fn get_name(&self) -> &'static str {
121        GROUP_LOCAL_ASSIGNMENT_RULE_NAME
122    }
123
124    fn serialize_to_properties(&self) -> RuleProperties {
125        RuleProperties::new()
126    }
127}
128
129#[cfg(test)]
130mod test {
131    use super::*;
132    use crate::rules::Rule;
133
134    use insta::assert_json_snapshot;
135
136    fn new_rule() -> GroupLocalAssignment {
137        GroupLocalAssignment::default()
138    }
139
140    #[test]
141    fn serialize_default_rule() {
142        let rule: Box<dyn Rule> = Box::new(new_rule());
143
144        assert_json_snapshot!("default_group_local_assignment", rule);
145    }
146}