lib/temporalio/converters/payload_converter/composite.rb



# frozen_string_literal: true

require 'temporalio/api'
require 'temporalio/converters/payload_converter'
require 'temporalio/converters/raw_value'

module Temporalio
  module Converters
    class PayloadConverter
      # Payload converter that is a collection of {Encoding}s. When converting to a payload, it tries each encoding
      # converter in order until one works. The encoding converter is expected to set the +encoding+ metadata which is
      # then used to match to the proper encoding converter when converting back to a Ruby value.
      class Composite < PayloadConverter
        class ConverterNotFound < Error; end
        class EncodingNotSet < Error; end

        # @return [Hash<String, Encoding>] Encoding converters processed in order.
        attr_reader :converters

        # Create a payload converter with the given encoding converters processed in order.
        #
        # @param converters [Array<Encoding>] Encoding converters.
        def initialize(*converters)
          super()
          @converters = converters.each_with_object({}) do |converter, result|
            result[converter.encoding] = converter
            result
          end
          @converters.freeze
        end

        # Convert Ruby value to a payload by going over each encoding converter in order until one can convert.
        #
        # @param value [Object] Ruby value to convert.
        # @param hint [Object, nil] Hint, if any, to assist conversion.
        # @return [Api::Common::V1::Payload] Converted payload.
        # @raise [ConverterNotFound] If no converters can process the value.
        def to_payload(value, hint: nil)
          # As a special case, raw values just return the payload within
          return value.payload if value.is_a?(RawValue)

          converters.each_value do |converter|
            payload = converter.to_payload(value, hint:)
            return payload unless payload.nil?
          end
          raise ConverterNotFound, "Value of type #{value} has no known converter"
        end

        # Convert payload to Ruby value based on its +encoding+ metadata on the payload.
        #
        # @param payload [Api::Common::V1::Payload] Payload to convert.
        # @param hint [Object, nil] Hint, if any, to assist conversion.
        # @return [Object] Converted Ruby value.
        # @raise [EncodingNotSet] If encoding not set on the metadata.
        # @raise [ConverterNotFound] If no converter found for the encoding.
        def from_payload(payload, hint: nil)
          encoding = payload.metadata['encoding']
          raise EncodingNotSet, 'Missing payload encoding' unless encoding

          converter = converters[encoding]
          raise ConverterNotFound, "No converter for encoding #{encoding}" unless converter

          converter.from_payload(payload, hint:)
        end
      end
    end
  end
end