# frozen_string_literal: truerequire'concurrent/map'moduleDrymoduleTypesclassConstructor<Nominal# Function is used internally by Constructor types## @api privateclassFunction# Wrapper for unsafe coercion functions## @api privateclassSafe<Functiondefcall(input,&block)@fn.(input,&block)rescueNoMethodError,TypeError,ArgumentError=>eCoercionError.handle(e,&block)endend# Coercion via a method call on a known object## @api privateclassMethodCall<Function@cache=::Concurrent::Map.new# Choose or build the base class## @return [Function]defself.call_class(method,public,safe)@cache.fetch_or_store([method,public,safe].hash)doifpublic::Class.new(PublicCall)doincludePublicCall.call_interface(method,safe)endelsifsafePrivateCallelsePrivateSafeCallendendend# Coercion with a publicly accessible method call## @api privateclassPublicCall<MethodCall@interfaces=::Concurrent::Map.new# Choose or build the interface## @return [::Module]defself.call_interface(method,safe)@interfaces.fetch_or_store([method,safe].hash)do::Module.newdoifsafemodule_eval(<<~RUBY,__FILE__,__LINE__+1)
def call(input, &block)
@target.#{method}(input, &block)
end
RUBYelsemodule_eval(<<~RUBY,__FILE__,__LINE__+1)
def call(input, &block)
@target.#{method}(input)
rescue NoMethodError, TypeError, ArgumentError => error
CoercionError.handle(error, &block)
end
RUBYendendendendend# Coercion via a private method call## @api privateclassPrivateCall<MethodCalldefcall(input,&block)@target.send(@name,input,&block)endend# Coercion via an unsafe private method call## @api privateclassPrivateSafeCall<PrivateCalldefcall(input,&block)@target.send(@name,input)rescueNoMethodError,TypeError,ArgumentError=>eCoercionError.handle(e,&block)endend# @api private## @return [MethodCall]defself.[](fn,safe)public=fn.receiver.respond_to?(fn.name)MethodCall.call_class(fn.name,public,safe).new(fn)endattr_reader:target,:namedefinitialize(fn)super@target=fn.receiver@name=fn.nameenddefto_ast[:method,target,name]endend# Choose or build specialized invokation code for a callable## @param [#call] fn# @return [Function]defself.[](fn)raiseArgumentError,'Missing constructor block'iffn.nil?iffn.is_a?(Function)fnelsiffn.is_a?(::Method)MethodCall[fn,yields_block?(fn)]elsifyields_block?(fn)new(fn)elseSafe.new(fn)endend# @return [Boolean]defself.yields_block?(fn)*,(last_arg,)=iffn.respond_to?(:parameters)fn.parameterselsefn.method(:call).parametersendlast_arg.equal?(:block)endincludeDry::Equalizer(:fn,immutable: true)attr_reader:fndefinitialize(fn)@fn=fnend# @return [Object]defcall(input,&block)@fn.(input,&block)endalias_method:[],:call# @return [Array]defto_astiffn.is_a?(::Proc)[:id,Dry::Types::FnContainer.register(fn)]else[:callable,fn]endendifRUBY_VERSION>='2.6'# @return [Function]def>>(other)proc=other.is_a?(::Proc)?other:other.fnFunction[@fn>>proc]end# @return [Function]def<<(other)proc=other.is_a?(::Proc)?other:other.fnFunction[@fn<<proc]endelse# @return [Function]def>>(other)proc=other.is_a?(::Proc)?other:other.fnFunction[->x{proc[@fn[x]]}]end# @return [Function]def<<(other)proc=other.is_a?(::Proc)?other:other.fnFunction[->x{@fn[proc[x]]}]endendendendendend