lib/canon/comparison/match_options/xml_resolver.rb



# frozen_string_literal: true

module Canon
  module Comparison
    module MatchOptions
      # XML/HTML-specific match options resolver
      class XmlResolver < BaseResolver
        # Format-specific defaults for XML/HTML
        #
        # Sensitive elements (preserve structural whitespace):
        # - XML: none by default — all structural whitespace stripped
        # - HTML: pre, code, textarea, script, style by default
        # Use preserve_whitespace_elements option to add elements that preserve whitespace.
        #
        FORMAT_DEFAULTS = {
          html: {
            preprocessing: :rendered,
            text_content: :normalize,
            structural_whitespace: :normalize,
            attribute_presence: :strict,
            attribute_order: :ignore,
            attribute_values: :strict,
            element_position: :ignore,
            comments: :ignore,
            whitespace_type: :strict,
          },
          xml: {
            preprocessing: :none,
            text_content: :strict,
            structural_whitespace: :strict,
            attribute_presence: :strict,
            attribute_order: :ignore,
            attribute_values: :strict,
            element_position: :strict,
            comments: :strict,
            whitespace_type: :strict,
          },
        }.freeze

        # Predefined match profiles for XML/HTML
        MATCH_PROFILES = {
          strict: {
            preprocessing: :none,
            text_content: :strict,
            structural_whitespace: :strict,
            attribute_presence: :strict,
            attribute_order: :strict,
            attribute_values: :strict,
            element_position: :strict,
            comments: :strict,
            whitespace_type: :strict,
          },

          rendered: {
            preprocessing: :none,
            text_content: :normalize,
            structural_whitespace: :normalize,
            attribute_presence: :strict,
            attribute_order: :strict,
            attribute_values: :strict,
            element_position: :strict,
            comments: :ignore,
            whitespace_type: :strict,
          },

          html4: {
            preprocessing: :rendered,
            text_content: :normalize,
            structural_whitespace: :normalize,
            attribute_presence: :strict,
            attribute_order: :strict,
            attribute_values: :normalize,
            element_position: :ignore,
            comments: :ignore,
            whitespace_type: :strict,
          },

          html5: {
            preprocessing: :rendered,
            text_content: :normalize,
            structural_whitespace: :normalize,
            attribute_presence: :strict,
            attribute_order: :strict,
            attribute_values: :strict,
            element_position: :ignore,
            comments: :ignore,
            whitespace_type: :strict,
          },

          spec_friendly: {
            preprocessing: :rendered,
            text_content: :normalize,
            structural_whitespace: :ignore,
            attribute_presence: :strict,
            attribute_order: :ignore,
            attribute_values: :normalize,
            element_position: :ignore,
            comments: :ignore,
            whitespace_type: :strict,
          },

          content_only: {
            preprocessing: :c14n,
            text_content: :normalize,
            structural_whitespace: :ignore,
            attribute_presence: :strict,
            attribute_order: :ignore,
            attribute_values: :normalize,
            element_position: :ignore,
            comments: :ignore,
            whitespace_type: :strict,
          },
        }.freeze

        class << self
          # Get format-specific default options
          def format_defaults(format)
            FORMAT_DEFAULTS[format]&.dup || FORMAT_DEFAULTS[:xml].dup
          end

          # Get options for a named profile
          def get_profile_options(profile)
            unless MATCH_PROFILES.key?(profile)
              raise Canon::Error,
                    "Unknown match profile: #{profile}. " \
                    "Valid profiles: #{MATCH_PROFILES.keys.join(', ')}"
            end
            MATCH_PROFILES[profile].dup
          end

          protected

          def dimension_set
            Canon::Comparison::Dimensions::Registry.for(:xml)
          end
        end
      end
    end
  end
end