File: constrained.rb

package info (click to toggle)
ruby-dry-types 1.2.2-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, forky, sid, trixie
  • size: 504 kB
  • sloc: ruby: 3,059; makefile: 4
file content (146 lines) | stat: -rw-r--r-- 3,145 bytes parent folder | download
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