# frozen_string_literal: true module Mustermann # Class for pattern objects composed of multiple patterns using binary logic. # @see Mustermann::Pattern#& # @see Mustermann::Pattern#| # @see Mustermann::Pattern#^ class Composite < Pattern attr_reader :patterns, :operator supported_options :operator, :type # @see Mustermann::Pattern.supported? def self.supported?(option, type: nil, **options) return true if super Mustermann[type || Mustermann::DEFAULT_TYPE].supported?(option, **options) end # @return [Mustermann::Pattern] a new composite pattern def self.new(*patterns, **options) patterns = patterns.flatten case patterns.size when 0 then raise ArgumentError, 'cannot create empty composite pattern' when 1 then patterns.first else super(patterns, **options) end end def initialize(patterns, operator: :|, **options) @operator = operator.to_sym @patterns = patterns.flat_map { |p| patterns_from(p, **options) } end # @see Mustermann::Pattern#== def ==(pattern) patterns == patterns_from(pattern) end # @see Mustermann::Pattern#eql? def eql?(pattern) patterns.eql? patterns_from(pattern) end # @see Mustermann::Pattern#hash def hash patterns.hash | operator.hash end # @see Mustermann::Pattern#=== def ===(string) patterns.map { |p| p === string }.inject(operator) end # @see Mustermann::Pattern#params def params(string) with_matching(string, :params) end # @see Mustermann::Pattern#match def match(string) with_matching(string, :match) end # @!visibility private def respond_to_special?(method) return false unless operator == :| patterns.all? { |p| p.respond_to?(method) } end # (see Mustermann::Pattern#expand) def expand(behavior = nil, values = {}) raise NotImplementedError, 'expanding not supported' unless respond_to? :expand @expander ||= Mustermann::Expander.new(*patterns) @expander.expand(behavior, values) end # (see Mustermann::Pattern#to_templates) def to_templates raise NotImplementedError, 'template generation not supported' unless respond_to? :to_templates patterns.flat_map(&:to_templates).uniq end # @return [String] the string representation of the pattern def to_s simple_inspect end # @!visibility private def inspect "#<%p:%s>" % [self.class, simple_inspect] end # @!visibility private def simple_inspect pattern_strings = patterns.map { |p| p.simple_inspect } "(#{pattern_strings.join(" #{operator} ")})" end # @!visibility private def with_matching(string, method) return unless self === string pattern = patterns.detect { |p| p === string } pattern.public_send(method, string) if pattern end # @!visibility private def patterns_from(pattern, **options) return pattern.patterns if pattern.is_a? Composite and pattern.operator == self.operator [options.empty? && pattern.is_a?(Pattern) ? pattern : Mustermann.new(pattern, **options)] end private :with_matching, :patterns_from end end