lib/dry/schema/predicate_inferrer.rb in dry-schema-1.2.0 vs lib/dry/schema/predicate_inferrer.rb in dry-schema-1.3.0

- old
+ new

@@ -20,13 +20,19 @@ TrueClass => :true?, BigDecimal => :decimal? }.freeze REDUCED_TYPES = { - %i[true? false?] => :bool? + [[[:true?], [:false?]]] => %i[bool?] }.freeze + HASH = %i[hash?].freeze + + ARRAY = %i[array?].freeze + + NIL = %i[nil?].freeze + # Compiler reduces type AST into a list of predicates # # @api private class Compiler # @return [PredicateRegistry] @@ -38,11 +44,11 @@ @registry = registry end # @api private def infer_predicate(type) - TYPE_TO_PREDICATE.fetch(type) { :"#{type.name.split('::').last.downcase}?" } + [TYPE_TO_PREDICATE.fetch(type) { :"#{type.name.split('::').last.downcase}?" }] end # @api private def visit(node) meth, rest = node @@ -52,25 +58,25 @@ # @api private def visit_nominal(node) type = node[0] predicate = infer_predicate(type) - if registry.key?(predicate) + if registry.key?(predicate[0]) predicate else - { type?: type } + [type?: type] end end # @api private def visit_hash(_) - :hash? + HASH end # @api private def visit_array(_) - :array? + ARRAY end # @api private def visit_lax(node) visit(node) @@ -88,31 +94,78 @@ visit(other) end # @api private def visit_sum(node) - left, right = node + left_node, right_node, = node + left = visit(left_node) + right = visit(right_node) - predicates = [visit(left), visit(right)] - - if predicates.first == :nil? - predicates[1..predicates.size - 1] + if left.eql?(NIL) + right else - predicates + [[left, right]] end end # @api private def visit_constrained(node) - other, * = node - visit(other) + other, rules = node + predicates = visit(rules) + + if predicates.empty? + visit(other) + else + [*visit(other), *merge_predicates(predicates)] + end end # @api private def visit_any(_) - nil + EMPTY_ARRAY end + + # @api private + def visit_and(node) + left, right = node + visit(left) + visit(right) + end + + # @api private + def visit_predicate(node) + pred, args = node + + if pred.equal?(:type?) + EMPTY_ARRAY + elsif registry.key?(pred) + *curried, _ = args + values = curried.map { |_, v| v } + + if values.empty? + [pred] + else + [pred => values[0]] + end + else + EMPTY_ARRAY + end + end + + private + + # @api private + def merge_predicates(nodes) + preds, merged = nodes.each_with_object([[], {}]) do |predicate, (ps, h)| + if predicate.is_a?(::Hash) + h.update(predicate) + else + ps << predicate + end + end + + merged.empty? ? preds : [*preds, merged] + end end # @return [Compiler] # @api private attr_reader :compiler @@ -132,10 +185,10 @@ predicates = compiler.visit(type.to_ast) if predicates.is_a?(Hash) predicates else - Array(REDUCED_TYPES[predicates] || predicates).flatten + REDUCED_TYPES[predicates] || predicates end end end end end