darklua_core/rules/
global_function_to_assign.rs

1use crate::nodes::{
2    AssignStatement, Block, FieldExpression, FunctionExpression, FunctionStatement, Identifier,
3    Statement, Variable,
4};
5use crate::process::{DefaultVisitor, NodeProcessor, NodeVisitor};
6use crate::rules::{
7    Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties,
8};
9
10use serde::ser::{Serialize, Serializer};
11use std::mem;
12
13use super::verify_no_rule_properties;
14
15struct Processor;
16
17impl Processor {
18    fn convert(&self, function: &mut FunctionStatement) -> Statement {
19        let mut function_expression = FunctionExpression::default();
20        function_expression.set_variadic(function.is_variadic());
21        mem::swap(function_expression.mutate_block(), function.mutate_block());
22        mem::swap(
23            function_expression.mutate_parameters(),
24            function.mutate_parameters(),
25        );
26
27        let name = function.get_name();
28
29        let base = name.get_name().clone();
30
31        let fields = name.get_field_names();
32
33        let variable = if fields.is_empty() {
34            if let Some(method) = name.get_method() {
35                Variable::from(FieldExpression::new(base, method.clone()))
36            } else {
37                Variable::from(base)
38            }
39        } else {
40            let mut fields_iter = fields.iter().chain(name.get_method()).map(Clone::clone);
41            let mut current = FieldExpression::new(base, fields_iter.next().unwrap());
42            for field in fields_iter {
43                current = FieldExpression::new(current, field.clone());
44            }
45            Variable::from(current)
46        };
47
48        if name.has_method() {
49            function_expression
50                .mutate_parameters()
51                .insert(0, Identifier::new("self").into());
52        }
53
54        AssignStatement::from_variable(variable, function_expression).into()
55    }
56}
57
58impl NodeProcessor for Processor {
59    fn process_statement(&mut self, statement: &mut Statement) {
60        if let Statement::Function(function) = statement {
61            let mut assign = self.convert(function);
62            mem::swap(statement, &mut assign)
63        };
64    }
65}
66
67pub const CONVERT_FUNCTION_TO_ASSIGNMENT_RULE_NAME: &str = "convert_function_to_assignment";
68
69/// Convert function statements into regular assignments.
70#[derive(Debug, Default, PartialEq, Eq)]
71pub struct ConvertFunctionToAssign {}
72
73impl FlawlessRule for ConvertFunctionToAssign {
74    fn flawless_process(&self, block: &mut Block, _: &Context) {
75        let mut processor = Processor;
76        DefaultVisitor::visit_block(block, &mut processor);
77    }
78}
79
80impl RuleConfiguration for ConvertFunctionToAssign {
81    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
82        verify_no_rule_properties(&properties)?;
83
84        Ok(())
85    }
86
87    fn get_name(&self) -> &'static str {
88        CONVERT_FUNCTION_TO_ASSIGNMENT_RULE_NAME
89    }
90
91    fn serialize_to_properties(&self) -> RuleProperties {
92        RuleProperties::new()
93    }
94}
95
96impl Serialize for ConvertFunctionToAssign {
97    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
98        serializer.serialize_str(CONVERT_FUNCTION_TO_ASSIGNMENT_RULE_NAME)
99    }
100}
101
102#[cfg(test)]
103mod test {
104    use super::*;
105
106    use crate::rules::Rule;
107
108    use insta::assert_json_snapshot;
109
110    fn new_rule() -> ConvertFunctionToAssign {
111        ConvertFunctionToAssign::default()
112    }
113
114    #[test]
115    fn serialize_default_rule() {
116        assert_json_snapshot!(new_rule(), @r###""convert_function_to_assignment""###);
117    }
118
119    #[test]
120    fn configure_with_extra_field_error() {
121        let result = json5::from_str::<Box<dyn Rule>>(
122            r#"{
123            rule: 'convert_function_to_assignment',
124            prop: "something",
125        }"#,
126        );
127        pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
128    }
129}