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
|
# frozen_string_literal: true
require 'dry/types/decorator'
module Dry
module Types
# Enum types can be used to define an enum on top of an existing type
#
# @api public
class Enum
include Type
include Dry::Equalizer(:type, :mapping, inspect: false, immutable: true)
include Decorator
include Builder
# @return [Array]
attr_reader :values
# @return [Hash]
attr_reader :mapping
# @return [Hash]
attr_reader :inverted_mapping
# @param [Type] type
# @param [Hash] options
# @option options [Array] :values
#
# @api private
def initialize(type, **options)
super
@mapping = options.fetch(:mapping).freeze
@values = @mapping.keys.freeze
@inverted_mapping = @mapping.invert.freeze
freeze
end
# @return [Object]
#
# @api private
def call_unsafe(input)
type.call_unsafe(map_value(input))
end
# @return [Object]
#
# @api private
def call_safe(input, &block)
type.call_safe(map_value(input), &block)
end
# @see Dry::Types::Constrained#try
#
# @api public
def try(input)
super(map_value(input))
end
# @api private
def default(*)
raise '.enum(*values).default(value) is not supported. Call '\
'.default(value).enum(*values) instead'
end
# Check whether a value is in the enum
alias_method :include?, :valid?
# @see Nominal#to_ast
#
# @api public
def to_ast(meta: true)
[:enum, [type.to_ast(meta: meta), mapping]]
end
# @return [String]
#
# @api public
def to_s
PRINTER.(self)
end
alias_method :inspect, :to_s
private
# Maps a value
#
# @param [Object] input
#
# @return [Object]
#
# @api private
def map_value(input)
if input.equal?(Undefined)
type.call
elsif mapping.key?(input)
input
else
inverted_mapping.fetch(input, input)
end
end
end
end
end
|