lib/dry/schema/extensions/hints/message_compiler_methods.rb
# frozen_string_literal: true
module Dry
module Schema
module Extensions
module Hints
# Adds support for processing [:hint, ...] nodes produced by dry-logic
#
# @api private
module MessageCompilerMethods
HINT_TYPE_EXCLUSION = %i[
key? nil? bool? str? int? float? decimal?
date? date_time? time? hash? array?
].freeze
HINT_OTHER_EXCLUSION = %i[format? filled?].freeze
# @api private
attr_reader :hints
# @api private
def initialize(*, **)
super
@hints = @options.fetch(:hints, true)
end
# @api private
def hints?
hints.equal?(true)
end
# @api private
def filter(messages, opts)
Array(messages).flatten.map { |msg| msg unless exclude?(msg, opts) }.compact.uniq
end
# @api private
def exclude?(messages, opts)
Array(messages).all? do |msg|
hints = opts
.hints
.reject { |hint| msg == hint }
.reject { |hint| hint.predicate == :filled? }
key_failure = opts.key_failure?(msg.path)
predicate = msg.predicate
(HINT_TYPE_EXCLUSION.include?(predicate) && !key_failure) ||
(msg.predicate == :filled? && key_failure) ||
(!key_failure && HINT_TYPE_EXCLUSION.include?(predicate) &&
!hints.empty? && hints.any? { |hint| hint.path == msg.path }) ||
HINT_OTHER_EXCLUSION.include?(predicate)
end
end
# @api private
def message_type(options)
options[:message_type].equal?(:hint) ? Hint : Message
end
# @api private
def visit_hint(node, opts)
if hints?
filter(visit(node, opts.(message_type: :hint)), opts)
end
end
# @api private
def visit_predicate(node, opts)
message = super
opts.current_messages << message
message
end
# @api private
def visit_each(_node, _opts)
# TODO: we can still generate a hint for elements here!
[]
end
end
end
end
end
end