# frozen_string_literal: true module Html2rss class Config ## # Holds the configurations of the selectors. class Selectors ITEMS_SELECTOR_NAME = :items # Struct to represent a selector with associated attributes for extraction and processing. Selector = Struct.new(:selector, :attribute, :extractor, :post_process, :order, :static, keyword_init: true) ## # @param config [Hash] def initialize(config) validate_config(config) @config = config end ## # @param name [Symbol] # @return [true, false] def selector?(name) name != ITEMS_SELECTOR_NAME && item_selector_names.include?(name) end ## # @param name [Symbol] # @return [Selector] def selector(name) raise ArgumentError, "invalid item's selector name: #{name}" unless selector?(name) Selector.new(config[name]) end ## # @return [Set] def category_selector_names selector_keys_for(:categories) end ## # @return [Set] def guid_selector_names selector_keys_for(:guid, default: :title_or_description) end ## # Returns the CSS/XPath selector. # # @param name [Symbol] # @return [String] def selector_string(name) Selector.new(config[name]).selector end ## # @return [Set] def item_selector_names @item_selector_names ||= config.keys.reject { |key| key == ITEMS_SELECTOR_NAME }.to_set end ## # @return [Symbol, nil] def items_order config.dig(ITEMS_SELECTOR_NAME, :order)&.to_sym end private attr_reader :config def validate_config(config) raise ArgumentError, 'selector for items is required' unless config[ITEMS_SELECTOR_NAME].is_a?(Hash) end ## # Returns the selector keys for the selector named `name`. If none, returns [default]. # # @param name [Symbol] # @param default [String, Symbol] # @return [Set] def selector_keys_for(name, default: nil) config.fetch(name) { Array(default) }.tap do |array| array.reject! { |entry| entry.to_s == '' } array.map!(&:to_sym) end.to_set end end end end