lib/dry/schema/predicate_inferrer.rb in dry-schema-0.2.0 vs lib/dry/schema/predicate_inferrer.rb in dry-schema-0.3.0

- old
+ new

@@ -1,5 +1,7 @@ +# frozen_string_literal: true + require 'dry/core/cache' module Dry module Schema # PredicateInferrer is used internally by `Macros::Value` @@ -7,43 +9,90 @@ # # @api private class PredicateInferrer extend Dry::Core::Cache - TYPE_TO_PREDICATE = Hash.new do |hash, type| - primitive = type.meta[:maybe] ? type.right.primitive : type.primitive - - if hash.key?(primitive) - hash[primitive] - else - :"#{primitive.name.split('::').last.downcase}?" - end - end - - TYPE_TO_PREDICATE.update( + TYPE_TO_PREDICATE = { FalseClass => :false?, Integer => :int?, NilClass => :nil?, String => :str?, TrueClass => :true? - ).freeze + }.freeze + REDUCED_TYPES = { + %i[true? false?] => :bool? + }.freeze + + # Compiler reduces type AST into a list of predicates + # + # @api private + class Compiler + def visit(node) + meth, rest = node + public_send(:"visit_#{meth}", rest) + end + + def visit_definition(node) + type = node[0] + + TYPE_TO_PREDICATE.fetch(type) { + :"#{type.name.split('::').last.downcase}?" + } + end + + def visit_array(*) + :array? + end + + def visit_safe(node) + other, * = node + visit(other) + end + + def visit_constructor(node) + other, * = node + visit(other) + end + + def visit_enum(node) + other, * = node + visit(other) + end + + def visit_sum(node) + left, right = node + + predicates = [visit(left), visit(right)] + + if predicates.first == :nil? + predicates[1..predicates.size - 1] + else + predicates + end + end + + def visit_constrained(node) + other, * = node + visit(other) + end + end + # Infer predicate identifier from the provided type # # @return [Symbol] # # @api private def self.[](type) fetch_or_store(type.hash) { - predicates = - if type.is_a?(Dry::Types::Sum) && !type.meta[:maybe] - [self[type.left], self[type.right]] - else - TYPE_TO_PREDICATE[type] - end - - Array(predicates).flatten + predicates = Array(compiler.visit(type.to_ast)).flatten + Array(REDUCED_TYPES[predicates] || predicates).flatten } + end + + # @api private + def self.compiler + @compiler ||= Compiler.new end end end end