module Lithic::Internal::Util

def self.monotonic_secs = Process.clock_gettime(Process::CLOCK_MONOTONIC)

Returns:
  • (Float) -

Other tags:
    Api: - private
def self.monotonic_secs = Process.clock_gettime(Process::CLOCK_MONOTONIC)

def arch

Returns:
  • (String) -

Other tags:
    Api: - private
def arch
  case (arch = RbConfig::CONFIG["arch"])&.downcase
  in nil
    "unknown"
  in /aarch64|arm64/
    "arm64"
  in /x86_64/
    "x64"
  in /arm/
    "arm"
  else
    "other:#{arch}"
  end
end

def chain_fused(enum, &blk)

Returns:
  • (Enumerable) -
    Other tags:
      Yieldparam: -

    Parameters:
    • blk (Proc) --
    • enum (Enumerable, nil) --
      Other tags:
        Api: - private
      def chain_fused(enum, &blk)
        iter = Enumerator.new { blk.call(_1) }
        fused_enum(iter) { close_fused!(enum) }
      end

      def close_fused!(enum)

      Parameters:
      • enum (Enumerable, nil) --
        Other tags:
          Api: - private
        def close_fused!(enum)
          return unless enum.is_a?(Enumerator)
          # rubocop:disable Lint/UnreachableLoop
          enum.rewind.each { break }
          # rubocop:enable Lint/UnreachableLoop
        end

        def coerce_boolean(input)

        Returns:
        • (Boolean, Object) -

        Parameters:
        • input (String, Boolean) --

        Other tags:
          Api: - private
        def coerce_boolean(input)
          case input.is_a?(String) ? input.downcase : input
          in "true"
            true
          in "false"
            false
          else
            input
          end
        end

        def coerce_boolean!(input)

        Returns:
        • (Boolean, nil) -

        Raises:
        • (ArgumentError) -

        Parameters:
        • input (String, Boolean) --

        Other tags:
          Api: - private
        def coerce_boolean!(input)
          case coerce_boolean(input)
          in true | false | nil => coerced
            coerced
          else
            raise ArgumentError.new("Unable to coerce #{input.inspect} into boolean value")
          end
        end

        def coerce_float(input)

        Returns:
        • (Float, Object) -

        Parameters:
        • input (String, Integer, Float) --

        Other tags:
          Api: - private
        def coerce_float(input)
          Float(input, exception: false) || input
        end

        def coerce_hash(input)

        Returns:
        • (Hash{Object=>Object}, Object) -

        Parameters:
        • input (Object) --

        Other tags:
          Api: - private
        def coerce_hash(input)
          case input
          in NilClass | Array | Set | Enumerator | StringIO | IO
            input
          else
            input.respond_to?(:to_h) ? input.to_h : input
          end
        end

        def coerce_hash!(input)

        Returns:
        • (Hash{Object=>Object}, nil) -

        Raises:
        • (ArgumentError) -

        Parameters:
        • input (Object) --

        Other tags:
          Api: - private
        def coerce_hash!(input)
          case coerce_hash(input)
          in Hash | nil => coerced
            coerced
          else
            message = "Expected a #{Hash} or #{Lithic::Internal::Type::BaseModel}, got #{data.inspect}"
            raise ArgumentError.new(message)
          end
        end

        def coerce_integer(input)

        Returns:
        • (Integer, Object) -

        Parameters:
        • input (String, Integer) --

        Other tags:
          Api: - private
        def coerce_integer(input)
          Integer(input, exception: false) || input
        end

        def decode_content(headers, stream:, suppress_error: false)

        Returns:
        • (Object) -

        Raises:
        • (JSON::ParserError) -

        Parameters:
        • suppress_error (Boolean) --
        • stream (Enumerable) --
        • headers (Hash{String=>String}, Net::HTTPHeader) --

        Other tags:
          Api: - private
        def decode_content(headers, stream:, suppress_error: false)
          case (content_type = headers["content-type"])
          in Lithic::Internal::Util::JSON_CONTENT
            json = stream.to_a.join
            begin
              JSON.parse(json, symbolize_names: true)
            rescue JSON::ParserError => e
              raise e unless suppress_error
              json
            end
          in Lithic::Internal::Util::JSONL_CONTENT
            lines = decode_lines(stream)
            chain_fused(lines) do |y|
              lines.each { y << JSON.parse(_1, symbolize_names: true) }
            end
          in %r{^text/event-stream}
            lines = decode_lines(stream)
            decode_sse(lines)
          else
            text = stream.to_a.join
            force_charset!(content_type, text: text)
            StringIO.new(text)
          end
        end

        def decode_lines(enum)

        Returns:
        • (Enumerable) -

        Parameters:
        • enum (Enumerable) --

        Other tags:
          Api: - private
        def decode_lines(enum)
          re = /(\r\n|\r|\n)/
          buffer = String.new
          cr_seen = nil
          chain_fused(enum) do |y|
            enum.each do |row|
              offset = buffer.bytesize
              buffer << row
              while (match = re.match(buffer, cr_seen&.to_i || offset))
                case [match.captures.first, cr_seen]
                in ["\r", nil]
                  cr_seen = match.end(1)
                  next
                in ["\r" | "\r\n", Integer]
                  y << buffer.slice!(..(cr_seen.pred))
                else
                  y << buffer.slice!(..(match.end(1).pred))
                end
                offset = 0
                cr_seen = nil
              end
            end
            y << buffer.slice!(..(cr_seen.pred)) unless cr_seen.nil?
            y << buffer unless buffer.empty?
          end
        end

        def decode_query(query)

        Returns:
        • (Hash{String=>Array}) -

        Parameters:
        • query (String, nil) --

        Other tags:
          Api: - private
        def decode_query(query)
          CGI.parse(query.to_s)
        end

        def decode_sse(lines)

        Returns:
        • (EnumerableObject}>) -

        Parameters:
        • lines (Enumerable) --

        Other tags:
          Api: - private
        def decode_sse(lines)
          # rubocop:disable Metrics/BlockLength
          chain_fused(lines) do |y|
            blank = {event: nil, data: nil, id: nil, retry: nil}
            current = {}
            lines.each do |line|
              case line.sub(/\R$/, "")
              in ""
                next if current.empty?
                y << {**blank, **current}
                current = {}
              in /^:/
                next
              in /^([^:]+):\s?(.*)$/
                field, value = Regexp.last_match.captures
                case field
                in "event"
                  current.merge!(event: value)
                in "data"
                  (current[:data] ||= String.new) << (value << "\n")
                in "id" unless value.include?("\0")
                  current.merge!(id: value)
                in "retry" if /^\d+$/ =~ value
                  current.merge!(retry: Integer(value))
                else
                end
              else
              end
            end
            # rubocop:enable Metrics/BlockLength
            y << {**blank, **current} unless current.empty?
          end
        end

        def deep_merge(*values, sentinel: nil, concat: false)

        Returns:
        • (Object) -

        Parameters:
        • concat (Boolean) -- whether to merge sequences by concatenation.
        • sentinel (Object, nil) -- the value to return if no values are provided.
        • values (Array) --
          Other tags:
            Api: - private
          def deep_merge(*values, sentinel: nil, concat: false)
            case values
            in [value, *values]
              values.reduce(value) do |acc, val|
                deep_merge_lr(acc, val, concat: concat)
              end
            else
              sentinel
            end
          end

          def deep_merge_lr(lhs, rhs, concat: false)

          Returns:
          • (Object) -

          Parameters:
          • concat (Boolean) --
          • rhs (Object) --
          • lhs (Object) --

          Other tags:
            Api: - private
          def deep_merge_lr(lhs, rhs, concat: false)
          lhs, rhs, concat]
          sh, Hash, _]
          merge(rhs) { deep_merge_lr(_2, _3, concat: concat) }
          ray, Array, true]
          concat(rhs)

          def dig(data, pick, &blk)

          Returns:
          • (Object, nil) -

          Parameters:
          • blk (Proc, nil) --
          • pick (Symbol, Integer, Array, Proc, nil) --
          • data (Hash{Symbol=>Object}, Array, Object) --
            Other tags:
              Api: - private
            def dig(data, pick, &blk)
              case [data, pick]
              in [_, nil]
                data
              in [Hash, Symbol] | [Array, Integer]
                data.fetch(pick) { blk&.call }
              in [Hash | Array, Array]
                pick.reduce(data) do |acc, key|
                  case acc
                  in Hash if acc.key?(key)
                    acc.fetch(key)
                  in Array if key.is_a?(Integer) && key < acc.length
                    acc[key]
                  else
                    return blk&.call
                  end
                end
              in [_, Proc]
                pick.call(data)
              else
                blk&.call
              end
            end

            def encode_content(headers, body)

            Returns:
            • (Object) -

            Parameters:
            • body (Object) --
            • headers (Hash{String=>String}) --

            Other tags:
              Api: - private
            def encode_content(headers, body)
              content_type = headers["content-type"]
              case [content_type, body]
              in [Lithic::Internal::Util::JSON_CONTENT, Hash | Array | -> { primitive?(_1) }]
                [headers, JSON.generate(body)]
              in [Lithic::Internal::Util::JSONL_CONTENT, Enumerable] unless body.is_a?(Lithic::Internal::Type::FileInput)
                [headers, body.lazy.map { JSON.generate(_1) }]
              in [%r{^multipart/form-data}, Hash | Lithic::Internal::Type::FileInput]
                boundary, strio = encode_multipart_streaming(body)
                headers = {**headers, "content-type" => "#{content_type}; boundary=#{boundary}"}
                [headers, strio]
              in [_, Symbol | Numeric]
                [headers, body.to_s]
              in [_, StringIO]
                [headers, body.string]
              in [_, Lithic::FilePart]
                [headers, body.content]
              else
                [headers, body]
              end
            end

            def encode_multipart_streaming(body)

            Returns:
            • (Array(String, Enumerable)) -

            Parameters:
            • body (Object) --

            Other tags:
              Api: - private
            def encode_multipart_streaming(body)
            ry = SecureRandom.urlsafe_base64(60)
            g = []
            = writable_enum do |y|
             body
            ash
            dy.each do |key, val|
            case val
            in Array if val.all? { primitive?(_1) }
              val.each do |v|
                write_multipart_chunk(y, boundary: boundary, key: key, val: v, closing: closing)
              end
            else
              write_multipart_chunk(y, boundary: boundary, key: key, val: val, closing: closing)
            end
            d
            
            ite_multipart_chunk(y, boundary: boundary, key: nil, val: body, closing: closing)
             "--#{boundary}--\r\n"
            io = fused_enum(strio) { closing.each(&:call) }
            ary, fused_io]

            def encode_query(query)

            Returns:
            • (String, nil) -

            Parameters:
            • query (Hash{String=>Array, String, nil}, nil) --

            Other tags:
              Api: - private
            def encode_query(query)
              query.to_h.empty? ? nil : URI.encode_www_form(query)
            end

            def force_charset!(content_type, text:)

            Parameters:
            • text (String) --
            • content_type (String) --

            Other tags:
              Api: - private
            def force_charset!(content_type, text:)
              charset = /charset=([^;\s]+)/.match(content_type)&.captures&.first
              return unless charset
              begin
                encoding = Encoding.find(charset)
                text.force_encoding(encoding)
              rescue ArgumentError
                nil
              end
            end

            def fused_enum(enum, external: false, &close)

            Returns:
            • (Enumerable) -
              Parameters:
              • close (Proc) --
              • external (Boolean) --
              • enum (Enumerable) --
                Other tags:
                  Api: - private
                def fused_enum(enum, external: false, &close)
                  fused = false
                  iter = Enumerator.new do |y|
                    next if fused
                    fused = true
                    if external
                      loop { y << enum.next }
                    else
                      enum.each(&y)
                    end
                  ensure
                    close&.call
                    close = nil
                  end
                  iter.define_singleton_method(:rewind) do
                    fused = true
                    self
                  end
                  iter
                end

                def interpolate_path(path)

                Returns:
                • (String) -

                Parameters:
                • path (String, Array) --

                Other tags:
                  Api: - private
                def interpolate_path(path)
                  case path
                  in String
                    path
                  in []
                    ""
                  in [String => p, *interpolations]
                    encoded = interpolations.map { ERB::Util.url_encode(_1) }
                    format(p, *encoded)
                  end
                end

                def join_parsed_uri(lhs, rhs)

                Returns:
                • (URI::Generic) -

                Options Hash: (**rhs)
                • :query (Hash{String=>Array}) --
                • :path (String, nil) --
                • :port (Integer, nil) --
                • :host (String, nil) --
                • :scheme (String, nil) --
                • :query (Hash{String=>Array}) --
                • :path (String, nil) --
                • :port (Integer, nil) --
                • :host (String, nil) --
                • :scheme (String, nil) --

                Parameters:
                • rhs (Hash{Symbol=>String, Integer, nil}) -- .
                • lhs (Hash{Symbol=>String, Integer, nil}) -- .

                Other tags:
                  Api: - private
                def join_parsed_uri(lhs, rhs)
                  base_path, base_query = lhs.fetch_values(:path, :query)
                  slashed = base_path.end_with?("/") ? base_path : "#{base_path}/"
                  parsed_path, parsed_query = parse_uri(rhs.fetch(:path)).fetch_values(:path, :query)
                  override = URI::Generic.build(**rhs.slice(:scheme, :host, :port), path: parsed_path)
                  joined = URI.join(URI::Generic.build(lhs.except(:path, :query)), slashed, override)
                  query = deep_merge(
                    joined.path == base_path ? base_query : {},
                    parsed_query,
                    rhs[:query].to_h,
                    concat: true
                  )
                  joined.query = encode_query(query)
                  joined
                end

                def normalized_headers(*headers)

                Returns:
                • (Hash{String=>String}) -

                Parameters:
                • headers (Hash{String=>String, Integer, Array, nil}) --

                Other tags:
                  Api: - private
                def normalized_headers(*headers)
                  {}.merge(*headers.compact).to_h do |key, val|
                    value =
                      case val
                      in Array
                        val.filter_map { _1&.to_s&.strip }.join(", ")
                      else
                        val&.to_s&.strip
                      end
                    [key.downcase, value]
                  end
                end

                def os

                Returns:
                • (String) -

                Other tags:
                  Api: - private
                def os
                  case (host = RbConfig::CONFIG["host_os"])&.downcase
                  in nil
                    "Unknown"
                  in /linux/
                    "Linux"
                  in /darwin/
                    "MacOS"
                  in /freebsd/
                    "FreeBSD"
                  in /openbsd/
                    "OpenBSD"
                  in /mswin|mingw|cygwin|ucrt/
                    "Windows"
                  else
                    "Other:#{host}"
                  end
                end

                def parse_uri(url)

                Returns:
                • (Hash{Symbol=>String, Integer, nil}) -

                Parameters:
                • url (URI::Generic, String) --

                Other tags:
                  Api: - private
                def parse_uri(url)
                  parsed = URI::Generic.component.zip(URI.split(url)).to_h
                  {**parsed, query: decode_query(parsed.fetch(:query))}
                end

                def primitive?(input)

                Returns:
                • (Boolean) -

                Parameters:
                • input (Object) --

                Other tags:
                  Api: - private
                def primitive?(input)
                  case input
                  in true | false | Numeric | Symbol | String
                    true
                  else
                    false
                  end
                end

                def unparse_uri(parsed)

                Returns:
                • (URI::Generic) -

                Options Hash: (**parsed)
                • :query (Hash{String=>Array}) --
                • :path (String, nil) --
                • :port (Integer, nil) --
                • :host (String, nil) --
                • :scheme (String, nil) --

                Parameters:
                • parsed (Hash{Symbol=>String, Integer, nil}) -- .

                Other tags:
                  Api: - private
                def unparse_uri(parsed)
                  URI::Generic.build(**parsed, query: encode_query(parsed.fetch(:query)))
                end

                def uri_origin(uri)

                Returns:
                • (String) -

                Parameters:
                • uri (URI::Generic) --

                Other tags:
                  Api: - private
                def uri_origin(uri)
                  "#{uri.scheme}://#{uri.host}#{uri.port == uri.default_port ? '' : ":#{uri.port}"}"
                end

                def writable_enum(&blk)

                Returns:
                • (Enumerable) -

                Other tags:
                  Yieldparam: -

                Parameters:
                • blk (Proc) --
                def writable_enum(&blk)
                  Enumerator.new do |y|
                    buf = String.new
                    y.define_singleton_method(:write) do
                      self << buf.replace(_1)
                      buf.bytesize
                    end
                    blk.call(y)
                  end
                end

                def write_multipart_chunk(y, boundary:, key:, val:, closing:)

                Parameters:
                • closing (Array) --
                • val (Object) --
                • key (Symbol, String) --
                • boundary (String) --
                • y (Enumerator::Yielder) --

                Other tags:
                  Api: - private
                def write_multipart_chunk(y, boundary:, key:, val:, closing:)
                --#{boundary}\r\n"
                Content-Disposition: form-data"
                 key.nil?
                 = ERB::Util.url_encode(key.to_s)
                 "; name=\"#{name}\""
                al
                hic::FilePart unless val.filename.nil?
                name = ERB::Util.url_encode(val.filename)
                 "; filename=\"#{filename}\""
                hname | IO
                name = ERB::Util.url_encode(::File.basename(val.to_path))
                 "; filename=\"#{filename}\""
                \r\n"
                multipart_content(y, val: val, closing: closing)

                def write_multipart_content(y, val:, closing:, content_type: nil)

                Parameters:
                • content_type (String, nil) --
                • closing (Array) --
                • val (Object) --
                • y (Enumerator::Yielder) --

                Other tags:
                  Api: - private
                def write_multipart_content(y, val:, closing:, content_type: nil)
                t_type ||= "application/octet-stream"
                al
                hic::FilePart
                rn write_multipart_content(
                
                l: val.content,
                osing: closing,
                ntent_type: val.content_type
                hname
                 "Content-Type: #{content_type}\r\n\r\n"
                 val.open(binmode: true)
                ing << io.method(:close)
                opy_stream(io, y)
                 "Content-Type: #{content_type}\r\n\r\n"
                opy_stream(val, y)
                ingIO
                 "Content-Type: #{content_type}\r\n\r\n"
                 val.string
                ing
                 "Content-Type: #{content_type}\r\n\r\n"
                 val.to_s
                { primitive?(_1) }
                 "Content-Type: text/plain\r\n\r\n"
                 val.to_s
                 "Content-Type: application/json\r\n\r\n"
                 JSON.generate(val)
                \r\n"