# 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)
# raised when an invalid selector name is used
class InvalidSelectorName < Html2rss::Error; end
##
# @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 InvalidSelectorName, "invalid selector name: #{name}" unless selector?(name)
keywords = config[name].slice(*available_keys)
if (additional_keys = available_keys - keywords.keys).any?
warn "additional keys (#{additional_keys.join(', ')}) present in selector #{name}"
end
Selector.new(keywords)
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
def available_keys = @available_keys ||= Selector.members
end
end
end