dry-types/lib/dry/types/builder.rb

141 lines
3.4 KiB
Ruby

# frozen_string_literal: true
require 'dry/core/deprecations'
module Dry
module Types
# Common API for building types and composition
#
# @api public
module Builder
include Dry::Core::Constants
# @return [Class]
#
# @api private
def constrained_type
Constrained
end
# @return [Class]
#
# @api private
def constructor_type
Constructor
end
# Compose two types into a Sum type
#
# @param [Type] other
#
# @return [Sum, Sum::Constrained]
#
# @api private
def |(other)
klass = constrained? && other.constrained? ? Sum::Constrained : Sum
klass.new(self, other)
end
# Turn a type into an optional type
#
# @return [Sum]
#
# @api public
def optional
Types['strict.nil'] | self
end
# Turn a type into a constrained type
#
# @param [Hash] options constraining rule (see {Types.Rule})
#
# @return [Constrained]
#
# @api public
def constrained(options)
constrained_type.new(self, rule: Types.Rule(options))
end
# Turn a type into a type with a default value
#
# @param [Object] input
# @param [Hash] options
# @param [#call,nil] block
#
# @raise [ConstraintError]
#
# @return [Default]
#
# @api public
def default(input = Undefined, options = EMPTY_HASH, &block)
unless input.frozen? || options[:shared]
where = Dry::Core::Deprecations::STACK.()
Dry::Core::Deprecations.warn(
"#{input.inspect} is mutable."\
' Be careful: types will return the same instance of the default'\
' value every time. Call `.freeze` when setting the default'\
' or pass `shared: true` to discard this warning.'\
"\n#{ where }",
tag: :'dry-types'
)
end
value = input.equal?(Undefined) ? block : input
if value.respond_to?(:call) || valid?(value)
Default[value].new(self, value)
else
raise ConstraintError.new("default value #{value.inspect} violates constraints", value)
end
end
# Define an enum on top of the existing type
#
# @param [Array] values
#
# @return [Enum]
#
# @api public
def enum(*values)
mapping =
if values.length == 1 && values[0].is_a?(::Hash)
values[0]
else
::Hash[values.zip(values)]
end
Enum.new(constrained(included_in: mapping.keys), mapping: mapping)
end
# Turn a type into a lax type that will rescue from type-errors and
# return the original input
#
# @return [Lax]
#
# @api public
def lax
Lax.new(self)
end
# Define a constructor for the type
#
# @param [#call,nil] constructor
# @param [Hash] options
# @param [#call,nil] block
#
# @return [Constructor]
#
# @api public
def constructor(constructor = nil, **options, &block)
constructor_type.new(with(options), fn: constructor || block)
end
end
end
end
require 'dry/types/default'
require 'dry/types/constrained'
require 'dry/types/enum'
require 'dry/types/lax'
require 'dry/types/sum'