darklua_core/rules/
convert_index_to_field.rs1use crate::nodes::{
2 Block, Expression, FieldExpression, Identifier, IndexExpression, Prefix, TableEntry,
3 TableExpression, TableFieldEntry, Variable,
4};
5use crate::process::utils::is_valid_identifier;
6use crate::process::{DefaultVisitor, Evaluator, LuaValue, NodeProcessor, NodeVisitor};
7use crate::rules::{
8 Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties,
9};
10
11use super::verify_no_rule_properties;
12
13use std::mem;
14
15#[derive(Debug, Clone, Default)]
16struct Converter {
17 evaluator: Evaluator,
18}
19
20impl Converter {
21 #[inline]
22 fn convert_index_to_field(&self, index: &IndexExpression) -> Option<FieldExpression> {
23 self.convert_to_field(index.get_index())
24 .map(|key| FieldExpression::new(index.get_prefix().clone(), Identifier::new(key)))
25 }
26
27 fn convert_to_field(&self, key_expression: &Expression) -> Option<String> {
28 if let LuaValue::String(string) = self.evaluator.evaluate(key_expression) {
29 String::from_utf8(string)
30 .ok()
31 .filter(|string| is_valid_identifier(string))
32 } else {
33 None
34 }
35 }
36}
37
38impl NodeProcessor for Converter {
39 fn process_expression(&mut self, expression: &mut Expression) {
40 let field: Option<Expression> = if let Expression::Index(index) = expression {
41 self.convert_index_to_field(index).map(Into::into)
42 } else {
43 None
44 };
45 if let Some(mut field) = field {
46 mem::swap(expression, &mut field);
47 }
48 }
49
50 fn process_prefix_expression(&mut self, prefix: &mut Prefix) {
51 let field: Option<Prefix> = if let Prefix::Index(index) = prefix {
52 self.convert_index_to_field(index).map(Into::into)
53 } else {
54 None
55 };
56 if let Some(mut field) = field {
57 mem::swap(prefix, &mut field);
58 }
59 }
60
61 fn process_variable(&mut self, variable: &mut Variable) {
62 let field: Option<Variable> = if let Variable::Index(index) = variable {
63 self.convert_index_to_field(index).map(Into::into)
64 } else {
65 None
66 };
67 if let Some(mut field) = field {
68 mem::swap(variable, &mut field);
69 }
70 }
71
72 fn process_table_expression(&mut self, table: &mut TableExpression) {
73 for entry in table.iter_mut_entries() {
74 let replace_with = match entry {
75 TableEntry::Index(entry) => self
76 .convert_to_field(entry.get_key())
77 .map(|key| TableFieldEntry::new(key, entry.get_value().clone()))
78 .map(TableEntry::from),
79
80 TableEntry::Field(_) | TableEntry::Value(_) => None,
81 };
82 if let Some(new_entry) = replace_with {
83 *entry = new_entry;
84 }
85 }
86 }
87}
88
89pub const CONVERT_INDEX_TO_FIELD_RULE_NAME: &str = "convert_index_to_field";
90
91#[derive(Debug, Default, PartialEq, Eq)]
93pub struct ConvertIndexToField {}
94
95impl FlawlessRule for ConvertIndexToField {
96 fn flawless_process(&self, block: &mut Block, _: &Context) {
97 let mut processor = Converter::default();
98 DefaultVisitor::visit_block(block, &mut processor);
99 }
100}
101
102impl RuleConfiguration for ConvertIndexToField {
103 fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
104 verify_no_rule_properties(&properties)?;
105
106 Ok(())
107 }
108
109 fn get_name(&self) -> &'static str {
110 CONVERT_INDEX_TO_FIELD_RULE_NAME
111 }
112
113 fn serialize_to_properties(&self) -> RuleProperties {
114 RuleProperties::new()
115 }
116}
117
118#[cfg(test)]
119mod test {
120 use super::*;
121 use crate::rules::Rule;
122
123 use insta::assert_json_snapshot;
124
125 fn new_rule() -> ConvertIndexToField {
126 ConvertIndexToField::default()
127 }
128
129 #[test]
130 fn serialize_default_rule() {
131 let rule: Box<dyn Rule> = Box::new(new_rule());
132
133 assert_json_snapshot!("default_convert_index_to_field", rule);
134 }
135
136 #[test]
137 fn configure_with_extra_field_error() {
138 let result = json5::from_str::<Box<dyn Rule>>(
139 r#"{
140 rule: 'convert_index_to_field',
141 prop: "something",
142 }"#,
143 );
144 pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'");
145 }
146}