# frozen_string_literal: true module Synvert::Core::NodeQuery::Compiler # Compare acutal value with expected value. module Comparable SIMPLE_VALID_OPERATORS = ['==', '!=', 'includes'] STRING_VALID_OPERATORS = ['==', '!=', '^=', '$=', '*=', 'includes'] NUMBER_VALID_OPERATORS = ['==', '!=', '>', '>=', '<', '<=', 'includes'] ARRAY_VALID_OPERATORS = ['==', '!=', 'in', 'not_in'] REGEXP_VALID_OPERATORS = ['=~', '!~'] # Check if the actual value matches the expected value. # # @param node [Parser::AST::Node] node to calculate actual value # @param operator [Symbol] operator to compare with expected value, operator can be '==', '!=', '>', '>=', '<', '<=', 'includes', 'in', 'not_in', '=~', '!~' # @return [Boolean] true if actual value matches the expected value # @raise [Synvert::Core::NodeQuery::Compiler::InvalidOperatorError] if operator is invalid def match?(node, operator) raise InvalidOperatorError, "invalid operator #{operator}" unless valid_operator?(operator) case operator when '!=' if expected_value.is_a?(::Array) actual = actual_value(node) !actual.is_a?(::Array) || actual.size != expected_value.size || actual.zip(expected_value).any? { |actual_node, expected_node| expected_node.match?(actual_node, '!=') } else actual_value(node) != expected_value end when '=~' actual_value(node) =~ expected_value when '!~' actual_value(node) !~ expected_value when '^=' actual_value(node).start_with?(expected_value) when '$=' actual_value(node).end_with?(expected_value) when '*=' actual_value(node).include?(expected_value) when '>' actual_value(node) > expected_value when '>=' actual_value(node) >= expected_value when '<' actual_value(node) < expected_value when '<=' actual_value(node) <= expected_value when 'in' expected_value.any? { |expected| expected.match?(node, '==') } when 'not_in' expected_value.all? { |expected| expected.match?(node, '!=') } when 'includes' actual_value(node).any? { |actual| actual == expected_value } else if expected_value.is_a?(::Array) actual = actual_value(node) actual.is_a?(::Array) && actual.size == expected_value.size && actual.zip(expected_value).all? { |actual_node, expected_node| expected_node.match?(actual_node, '==') } else actual_value(node) == expected_value end end end # Get the actual value from ast node. # @return if node is a {Parser::AST::Node}, return the node value, otherwise, return the node itself. def actual_value(node) if node.is_a?(::Parser::AST::Node) node.to_value else node end end # Get the expected value def expected_value @value end # Check if the operator is valid. # @return [Boolean] true if the operator is valid def valid_operator?(operator) valid_operators.include?(operator) end end end