Skip to main content

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, RuleMetadata, 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    metadata: RuleMetadata,
73}
74
75impl FlawlessRule for ConvertFunctionToAssign {
76    fn flawless_process(&self, block: &mut Block, _: &Context) {
77        let mut processor = Processor;
78        DefaultVisitor::visit_block(block, &mut processor);
79    }
80}
81
82impl RuleConfiguration for ConvertFunctionToAssign {
83    fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
84        verify_no_rule_properties(&properties)?;
85
86        Ok(())
87    }
88
89    fn get_name(&self) -> &'static str {
90        CONVERT_FUNCTION_TO_ASSIGNMENT_RULE_NAME
91    }
92
93    fn serialize_to_properties(&self) -> RuleProperties {
94        RuleProperties::new()
95    }
96
97    fn set_metadata(&mut self, metadata: RuleMetadata) {
98        self.metadata = metadata;
99    }
100
101    fn metadata(&self) -> &RuleMetadata {
102        &self.metadata
103    }
104}
105
106impl Serialize for ConvertFunctionToAssign {
107    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
108        serializer.serialize_str(CONVERT_FUNCTION_TO_ASSIGNMENT_RULE_NAME)
109    }
110}
111
112#[cfg(test)]
113mod test {
114    use super::*;
115
116    use crate::rules::Rule;
117
118    use insta::assert_json_snapshot;
119
120    fn new_rule() -> ConvertFunctionToAssign {
121        ConvertFunctionToAssign::default()
122    }
123
124    #[test]
125    fn serialize_default_rule() {
126        assert_json_snapshot!(new_rule(), @r###""convert_function_to_assignment""###);
127    }
128
129    #[test]
130    fn configure_with_extra_field_error() {
131        let result = json5::from_str::<Box<dyn Rule>>(
132            r#"{
133            rule: 'convert_function_to_assignment',
134            prop: "something",
135        }"#,
136        );
137        insta::assert_snapshot!(result.unwrap_err().to_string(), @"unexpected field 'prop' at line 1 column 1");
138    }
139}