|
| 1 | +use crate::nodes::{ |
| 2 | + AssignStatement, Block, FieldExpression, FunctionExpression, FunctionStatement, Identifier, |
| 3 | + Statement, Variable, |
| 4 | +}; |
| 5 | +use crate::process::{DefaultVisitor, NodeProcessor, NodeVisitor}; |
| 6 | +use crate::rules::{ |
| 7 | + Context, FlawlessRule, RuleConfiguration, RuleConfigurationError, RuleProperties, |
| 8 | +}; |
| 9 | + |
| 10 | +use serde::ser::{Serialize, Serializer}; |
| 11 | +use std::mem; |
| 12 | + |
| 13 | +use super::verify_no_rule_properties; |
| 14 | + |
| 15 | +struct Processor; |
| 16 | + |
| 17 | +impl 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 | + |
| 58 | +impl 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 | + |
| 67 | +pub 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)] |
| 71 | +pub struct ConvertFunctionToAssign {} |
| 72 | + |
| 73 | +impl 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 | + |
| 80 | +impl 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 | + |
| 96 | +impl 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)] |
| 103 | +mod 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 | +} |
0 commit comments