|
| 1 | +use crate::nodes::{ |
| 2 | + BinaryExpression, BinaryOperator, Block, Expression, IndexExpression, TableEntry, |
| 3 | + TableExpression, |
| 4 | +}; |
| 5 | +use crate::process::{DefaultVisitor, Evaluator, NodeProcessor, NodeVisitor}; |
| 6 | +use crate::rules::{ |
| 7 | + Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties, |
| 8 | +}; |
| 9 | + |
| 10 | +use super::verify_no_rule_properties; |
| 11 | + |
| 12 | +#[derive(Default)] |
| 13 | +struct Processor { |
| 14 | + evaluator: Evaluator, |
| 15 | +} |
| 16 | + |
| 17 | +impl Processor { |
| 18 | + fn wrap_in_table(&self, expression: Expression) -> Expression { |
| 19 | + TableExpression::new(vec![TableEntry::Value({ |
| 20 | + if self.evaluator.can_return_multiple_values(&expression) { |
| 21 | + expression.in_parentheses() |
| 22 | + } else { |
| 23 | + expression |
| 24 | + } |
| 25 | + })]) |
| 26 | + .into() |
| 27 | + } |
| 28 | + |
| 29 | + fn convert_if_branch( |
| 30 | + &self, |
| 31 | + condition: Expression, |
| 32 | + result: Expression, |
| 33 | + else_result: Expression, |
| 34 | + ) -> Expression { |
| 35 | + if self |
| 36 | + .evaluator |
| 37 | + .evaluate(&result) |
| 38 | + .is_truthy() |
| 39 | + .unwrap_or_default() |
| 40 | + { |
| 41 | + BinaryExpression::new( |
| 42 | + BinaryOperator::Or, |
| 43 | + BinaryExpression::new(BinaryOperator::And, condition, result), |
| 44 | + else_result, |
| 45 | + ) |
| 46 | + .into() |
| 47 | + } else { |
| 48 | + IndexExpression::new( |
| 49 | + Expression::from(BinaryExpression::new( |
| 50 | + BinaryOperator::Or, |
| 51 | + BinaryExpression::new( |
| 52 | + BinaryOperator::And, |
| 53 | + condition, |
| 54 | + self.wrap_in_table(result), |
| 55 | + ), |
| 56 | + self.wrap_in_table(else_result), |
| 57 | + )), |
| 58 | + Expression::from(1), |
| 59 | + ) |
| 60 | + .into() |
| 61 | + } |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +impl NodeProcessor for Processor { |
| 66 | + fn process_expression(&mut self, expression: &mut Expression) { |
| 67 | + if let Expression::If(if_expression) = expression { |
| 68 | + let else_result = if_expression.iter_branches().fold( |
| 69 | + if_expression.get_else_result().clone(), |
| 70 | + |else_result, branch| { |
| 71 | + self.convert_if_branch( |
| 72 | + branch.get_condition().clone(), |
| 73 | + branch.get_result().clone(), |
| 74 | + else_result, |
| 75 | + ) |
| 76 | + }, |
| 77 | + ); |
| 78 | + |
| 79 | + *expression = self.convert_if_branch( |
| 80 | + if_expression.get_condition().clone(), |
| 81 | + if_expression.get_result().clone(), |
| 82 | + else_result, |
| 83 | + ); |
| 84 | + } |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +pub const REMOVE_IF_EXPRESSION_RULE_NAME: &str = "remove_if_expression"; |
| 89 | + |
| 90 | +/// A rule that removes trailing `nil` in local assignments. |
| 91 | +#[derive(Debug, Default, PartialEq, Eq)] |
| 92 | +pub struct RemoveIfExpression {} |
| 93 | + |
| 94 | +impl FlawlessRule for RemoveIfExpression { |
| 95 | + fn flawless_process(&self, block: &mut Block, _: &Context) { |
| 96 | + let mut processor = Processor::default(); |
| 97 | + DefaultVisitor::visit_block(block, &mut processor); |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +impl RuleConfiguration for RemoveIfExpression { |
| 102 | + fn configure(&mut self, properties: RuleProperties) -> Result<(), RuleConfigurationError> { |
| 103 | + verify_no_rule_properties(&properties)?; |
| 104 | + |
| 105 | + Ok(()) |
| 106 | + } |
| 107 | + |
| 108 | + fn get_name(&self) -> &'static str { |
| 109 | + REMOVE_IF_EXPRESSION_RULE_NAME |
| 110 | + } |
| 111 | + |
| 112 | + fn serialize_to_properties(&self) -> RuleProperties { |
| 113 | + RuleProperties::new() |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +#[cfg(test)] |
| 118 | +mod test { |
| 119 | + use super::*; |
| 120 | + use crate::rules::Rule; |
| 121 | + |
| 122 | + use insta::assert_json_snapshot; |
| 123 | + |
| 124 | + fn new_rule() -> RemoveIfExpression { |
| 125 | + RemoveIfExpression::default() |
| 126 | + } |
| 127 | + |
| 128 | + #[test] |
| 129 | + fn serialize_default_rule() { |
| 130 | + let rule: Box<dyn Rule> = Box::new(new_rule()); |
| 131 | + |
| 132 | + assert_json_snapshot!("default_remove_if_expression", rule); |
| 133 | + } |
| 134 | + |
| 135 | + #[test] |
| 136 | + fn configure_with_extra_field_error() { |
| 137 | + let result = json5::from_str::<Box<dyn Rule>>( |
| 138 | + r#"{ |
| 139 | + rule: 'remove_if_expression', |
| 140 | + prop: "something", |
| 141 | + }"#, |
| 142 | + ); |
| 143 | + pretty_assertions::assert_eq!(result.unwrap_err().to_string(), "unexpected field 'prop'"); |
| 144 | + } |
| 145 | +} |
0 commit comments