Skip to main content

darklua_core/rules/
no_local_function.rs

1use crate::nodes::{Block, FunctionAssignment, FunctionExpression, Statement, VariableAssignment};
2use crate::process::{processors::FindVariables, DefaultVisitor, NodeProcessor, NodeVisitor};
3use crate::rules::{
4    Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleMetadata, RuleProperties,
5};
6
7use serde::ser::{Serialize, Serializer};
8use std::mem;
9
10use super::verify_no_rule_properties;
11
12struct Processor;
13
14impl Processor {
15    fn convert(&self, local_function: &mut FunctionAssignment) -> Statement {
16        let kind = local_function.get_assignment_kind();
17        let mut function_expression = FunctionExpression::default();
18        function_expression.set_variadic(local_function.is_variadic());
19        mem::swap(
20            function_expression.mutate_block(),
21            local_function.mutate_block(),
22        );
23        mem::swap(
24            function_expression.mutate_parameters(),
25            local_function.mutate_parameters(),
26        );
27
28        VariableAssignment::from_variable(local_function.get_name())
29            .with_assignment_kind(kind)
30            .with_value(function_expression)
31            .into()
32    }
33}
34
35impl NodeProcessor for Processor {
36    fn process_statement(&mut self, statement: &mut Statement) {
37        if let Statement::LocalFunction(local_function) = statement {
38            let name = local_function.get_name().to_owned();
39
40            if local_function.has_parameter(&name) {
41                let mut assign = self.convert(local_function);
42                mem::swap(statement, &mut assign)
43            } else {
44                let mut find_usage = FindVariables::new(&name);
45                DefaultVisitor::visit_block(local_function.mutate_block(), &mut find_usage);
46
47                if !find_usage.has_found_usage() {
48                    let mut assign = self.convert(local_function);
49                    mem::swap(statement, &mut assign)
50                }
51            }
52        };
53    }
54}
55
56pub const CONVERT_LOCAL_FUNCTION_TO_ASSIGN_RULE_NAME: &str = "convert_local_function_to_assign";
57
58/// Convert local function statements into local assignements when the function is not recursive.
59#[derive(Debug, Default, PartialEq, Eq)]
60pub struct ConvertLocalFunctionToAssign {
61    metadata: RuleMetadata,
62}
63
64impl FlawlessRule for ConvertLocalFunctionToAssign {
65    fn flawless_process(&self, block: &mut Block, _: &Context) {
66        let mut processor = Processor;
67        DefaultVisitor::visit_block(block, &mut processor);
68    }
69}
70
71impl RuleConfiguration for ConvertLocalFunctionToAssign {
72    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
73        verify_no_rule_properties(&properties)?;
74
75        Ok(())
76    }
77
78    fn get_name(&self) -> &'static str {
79        CONVERT_LOCAL_FUNCTION_TO_ASSIGN_RULE_NAME
80    }
81
82    fn serialize_to_properties(&self) -> RuleProperties {
83        RuleProperties::new()
84    }
85
86    fn set_metadata(&mut self, metadata: RuleMetadata) {
87        self.metadata = metadata;
88    }
89
90    fn metadata(&self) -> &RuleMetadata {
91        &self.metadata
92    }
93}
94
95impl Serialize for ConvertLocalFunctionToAssign {
96    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
97        serializer.serialize_str(CONVERT_LOCAL_FUNCTION_TO_ASSIGN_RULE_NAME)
98    }
99}
100
101#[cfg(test)]
102mod test {
103    use super::*;
104
105    use crate::rules::Rule;
106
107    use insta::assert_json_snapshot;
108
109    fn new_rule() -> ConvertLocalFunctionToAssign {
110        ConvertLocalFunctionToAssign::default()
111    }
112
113    #[test]
114    fn serialize_default_rule() {
115        assert_json_snapshot!(new_rule(), @r###""convert_local_function_to_assign""###);
116    }
117
118    #[test]
119    fn configure_with_extra_field_error() {
120        let result = json5::from_str::<Box<dyn Rule>>(
121            r#"{
122            rule: 'convert_local_function_to_assign',
123            prop: "something",
124        }"#,
125        );
126        insta::assert_snapshot!(result.unwrap_err().to_string(), @"unexpected field 'prop' at line 1 column 1")
127    }
128}