1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
# frozen_string_literal: true
require 'dry/types/decorator'
require 'dry/types/constraints'
require 'dry/types/constrained/coercible'
module Dry
module Types
# Constrained types apply rules to the input
#
# @api public
class Constrained
include Type
include Decorator
include Builder
include Printable
include Dry::Equalizer(:type, :rule, inspect: false, immutable: true)
# @return [Dry::Logic::Rule]
attr_reader :rule
# @param [Type] type
#
# @param [Hash] options
#
# @api public
def initialize(type, **options)
super
@rule = options.fetch(:rule)
end
# @api private
#
# @return [Object]
#
# @api public
def call_unsafe(input)
result = rule.(input)
if result.success?
type.call_unsafe(input)
else
raise ConstraintError.new(result, input)
end
end
# @api private
#
# @return [Object]
#
# @api public
def call_safe(input, &block)
if rule[input]
type.call_safe(input, &block)
else
yield
end
end
# Safe coercion attempt. It is similar to #call with a
# block given but returns a Result instance with metadata
# about errors (if any).
#
# @overload try(input)
# @param [Object] input
# @return [Logic::Result]
#
# @overload try(input)
# @param [Object] input
# @yieldparam [Failure] failure
# @yieldreturn [Object]
# @return [Object]
#
# @api public
def try(input, &block)
result = rule.(input)
if result.success?
type.try(input, &block)
else
failure = failure(input, ConstraintError.new(result, input))
block_given? ? yield(failure) : failure
end
end
# @param [Hash] options
# The options hash provided to {Types.Rule} and combined
# using {&} with previous {#rule}
#
# @return [Constrained]
#
# @see Dry::Logic::Operators#and
#
# @api public
def constrained(options)
with(rule: rule & Types.Rule(options))
end
# @return [true]
#
# @api public
def constrained?
true
end
# @param [Object] value
#
# @return [Boolean]
#
# @api public
def ===(value)
valid?(value)
end
# Build lax type. Constraints are not applicable to lax types hence unwrapping
#
# @return [Lax]
# @api public
def lax
type.lax
end
# @see Nominal#to_ast
# @api public
def to_ast(meta: true)
[:constrained, [type.to_ast(meta: meta), rule.to_ast]]
end
# @api private
def constructor_type
type.constructor_type
end
private
# @param [Object] response
#
# @return [Boolean]
#
# @api private
def decorate?(response)
super || response.is_a?(Constructor)
end
end
end
end
|