# frozen_string_literal: true class BCDD::Result class Context < self require_relative 'context/failure' require_relative 'context/success' require_relative 'context/mixin' require_relative 'context/expectations' require_relative 'context/callable_and_then' EXPECTED_OUTCOME = 'BCDD::Result::Context::Success or BCDD::Result::Context::Failure' def self.Success(type, **value) Success.new(type: type, value: value) end def self.Failure(type, **value) Failure.new(type: type, value: value) end def initialize(type:, value:, source: nil, expectations: nil, terminal: nil) value.is_a?(::Hash) or raise ::ArgumentError, 'value must be a Hash' @acc = {} super end def and_then(method_name = nil, **injected_value, &block) super(method_name, injected_value, &block) end def and_then!(source, **injected_value) _call = injected_value.delete(:_call) acc.merge!(injected_value) super(source, injected_value, _call: _call) end protected attr_reader :acc private SourceMethodArity = ->(method) do return 0 if method.arity.zero? parameters = method.parameters.map(&:first) return 1 if !parameters.empty? && parameters.all?(/\Akey/) -1 end def call_and_then_source_method!(method, injected_value) acc.merge!(value.merge(injected_value)) case SourceMethodArity[method] when 0 then source.send(method.name) when 1 then source.send(method.name, **acc) else raise Error::InvalidSourceMethodArity.build(source: source, method: method, max_arity: 1) end end def call_and_then_block!(block) acc.merge!(value) block.call(acc) end def call_and_then_callable!(source, value:, injected_value:, method_name:) acc.merge!(value.merge(injected_value)) CallableAndThen::Caller.call(source, value: acc, injected_value: injected_value, method_name: method_name) end def ensure_result_object(result, origin:) raise_unexpected_outcome_error(result, origin) unless result.is_a?(Context) return result.tap { _1.acc.merge!(acc) } if result.source.equal?(source) raise Error::InvalidResultSource.build(given_result: result, expected_source: source) end def raise_unexpected_outcome_error(result, origin) raise Error::UnexpectedOutcome.build(outcome: result, origin: origin, expected: EXPECTED_OUTCOME) end private_constant :SourceMethodArity end end