darklua_core/rules/
remove_assertions.rs1use std::collections::HashMap;
2use std::iter::{self, FromIterator};
3
4use crate::nodes::{Block, Expression, FunctionCall, Prefix, TupleArguments};
5use crate::process::{IdentifierTracker, NodeVisitor, ScopeVisitor};
6use crate::rules::{
7 Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleMetadata, RuleProperties,
8};
9
10use super::remove_call_match::{CallMatch, RemoveFunctionCallProcessor};
11
12const ASSERT_FUNCTION_NAME: &str = "assert";
13
14pub const REMOVE_ASSERTIONS_RULE_NAME: &str = "remove_assertions";
15
16#[derive(Debug, PartialEq, Eq)]
18pub struct RemoveAssertions {
19 metadata: RuleMetadata,
20 preserve_args_side_effects: bool,
21}
22
23impl Default for RemoveAssertions {
24 fn default() -> Self {
25 Self {
26 metadata: RuleMetadata::default(),
27 preserve_args_side_effects: true,
28 }
29 }
30}
31
32struct AssertMatcher;
33
34impl CallMatch<()> for AssertMatcher {
35 fn matches(&self, identifiers: &IdentifierTracker, prefix: &Prefix) -> bool {
36 if identifiers.is_identifier_used(ASSERT_FUNCTION_NAME) {
37 return false;
38 }
39
40 match prefix {
41 Prefix::Identifier(identifier) => identifier.get_name() == ASSERT_FUNCTION_NAME,
42 _ => false,
43 }
44 }
45
46 fn compute_result(
47 &self,
48 call: &FunctionCall,
49 mappings: &HashMap<&'static str, String>,
50 ) -> Option<Expression> {
51 let expressions = call.get_arguments().clone().to_expressions();
52
53 Some(match expressions.len() {
54 0 => Expression::nil(),
55 1 => expressions
56 .into_iter()
57 .next()
58 .expect("at least one expression is expected"),
59 _ => FunctionCall::from_name(
60 mappings
61 .get("select")
62 .cloned()
63 .unwrap_or_else(|| "select".to_owned()),
64 )
65 .with_arguments(TupleArguments::from_iter(
66 iter::once(Expression::from(1)).chain(expressions),
67 ))
68 .into(),
69 })
70 }
71
72 fn reserve_globals(&self) -> impl Iterator<Item = &'static str> {
73 iter::once("select")
74 }
75}
76
77impl FlawlessRule for RemoveAssertions {
78 fn flawless_process(&self, block: &mut Block, _: &Context) {
79 let mut processor =
80 RemoveFunctionCallProcessor::new(self.preserve_args_side_effects, AssertMatcher);
81 ScopeVisitor::visit_block(block, &mut processor);
82
83 if let Some(statement) = processor.extract_reserved_globals() {
84 block.insert_statement(0, statement);
85 }
86 }
87}
88
89impl RuleConfiguration for RemoveAssertions {
90 fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> {
91 for (key, value) in properties {
92 match key.as_str() {
93 "preserve_arguments_side_effects" => {
94 self.preserve_args_side_effects = value.expect_bool(&key)?;
95 }
96 _ => return Err(RuleConfigurationError::UnexpectedProperty(key)),
97 }
98 }
99
100 Ok(())
101 }
102
103 fn get_name(&self) -> &'static str {
104 REMOVE_ASSERTIONS_RULE_NAME
105 }
106
107 fn serialize_to_properties(&self) -> RuleProperties {
108 let mut properties = RuleProperties::new();
109
110 if !self.preserve_args_side_effects {
111 properties.insert("preserve_arguments_side_effects".to_owned(), false.into());
112 }
113
114 properties
115 }
116
117 fn set_metadata(&mut self, metadata: RuleMetadata) {
118 self.metadata = metadata;
119 }
120
121 fn metadata(&self) -> &RuleMetadata {
122 &self.metadata
123 }
124}
125
126#[cfg(test)]
127mod test {
128 use super::*;
129 use crate::rules::Rule;
130
131 use insta::assert_json_snapshot;
132
133 fn new_rule() -> RemoveAssertions {
134 RemoveAssertions::default()
135 }
136
137 #[test]
138 fn serialize_default_rule() {
139 let rule: Box<dyn Rule> = Box::new(new_rule());
140
141 assert_json_snapshot!(rule, @r###""remove_assertions""###);
142 }
143
144 #[test]
145 fn serialize_rule_without_side_effects() {
146 let rule: Box<dyn Rule> = Box::new(RemoveAssertions {
147 metadata: RuleMetadata::default(),
148 preserve_args_side_effects: false,
149 });
150
151 assert_json_snapshot!(rule, @r###"
152 {
153 "rule": "remove_assertions",
154 "preserve_arguments_side_effects": false
155 }
156 "###);
157 }
158
159 #[test]
160 fn configure_with_extra_field_error() {
161 let result = json5::from_str::<Box<dyn Rule>>(
162 r#"{
163 rule: 'remove_assertions',
164 prop: "something",
165 }"#,
166 );
167 insta::assert_snapshot!(result.unwrap_err().to_string(), @"unexpected field 'prop' at line 1 column 1");
168 }
169}