Module: EInvoiceAPI::Internal::Util Private

Extended by:
SorbetRuntimeSupport
Defined in:
lib/e_invoice_api/internal/util.rb

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

Defined Under Namespace

Modules: SorbetRuntimeSupport Classes: ReadIOAdapter

Constant Summary collapse

RFC_3986_NOT_PCHARS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

www.rfc-editor.org/rfc/rfc3986.html#section-3.3

/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/
JSON_CONTENT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%r{^application/(?:[a-zA-Z0-9.-]+\+)?json(?!l)}
JSONL_CONTENT =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%r{^application/(:?x-(?:n|l)djson)|(:?(?:x-)?jsonl)}

Class Method Summary collapse

Methods included from SorbetRuntimeSupport

const_missing, define_sorbet_constant!, sorbet_constant_defined?, to_sorbet_type, to_sorbet_type

Class Method Details

.archString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (String)


33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/e_invoice_api/internal/util.rb', line 33

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

.chain_fused(enum, &blk) {|| ... } ⇒ Enumerable<Object>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • enum (Enumerable<Object>, nil)
  • blk (Proc)

Yield Parameters:

  • (Enumerator::Yielder)

Returns:

  • (Enumerable<Object>)


783
784
785
786
# File 'lib/e_invoice_api/internal/util.rb', line 783

def chain_fused(enum, &blk)
  iter = Enumerator.new { blk.call(_1) }
  fused_enum(iter) { close_fused!(enum) }
end

.close_fused!(enum) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • enum (Enumerable<Object>, nil)


768
769
770
771
772
773
774
# File 'lib/e_invoice_api/internal/util.rb', line 768

def close_fused!(enum)
  return unless enum.is_a?(Enumerator)

  # rubocop:disable Lint/UnreachableLoop
  enum.rewind.each { break }
  # rubocop:enable Lint/UnreachableLoop
end

.coerce_boolean(input) ⇒ Boolean, Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • input (String, Boolean)

Returns:

  • (Boolean, Object)


91
92
93
94
95
96
97
98
99
100
# File 'lib/e_invoice_api/internal/util.rb', line 91

def coerce_boolean(input)
  case input.is_a?(String) ? input.downcase : input
  in "true"
    true
  in "false"
    false
  else
    input
  end
end

.coerce_boolean!(input) ⇒ Boolean?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • input (String, Boolean)

Returns:

  • (Boolean, nil)

Raises:

  • (ArgumentError)


108
109
110
111
112
113
114
115
# File 'lib/e_invoice_api/internal/util.rb', line 108

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

.coerce_float(input) ⇒ Float, Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • input (String, Integer, Float)

Returns:

  • (Float, Object)


131
132
133
# File 'lib/e_invoice_api/internal/util.rb', line 131

def coerce_float(input)
  Float(input, exception: false) || input
end

.coerce_hash(input) ⇒ Hash{Object=>Object}, Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • input (Object)

Returns:

  • (Hash{Object=>Object}, Object)


140
141
142
143
144
145
146
147
# File 'lib/e_invoice_api/internal/util.rb', line 140

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

.coerce_hash!(input) ⇒ Hash{Object=>Object}?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • input (Object)

Returns:

  • (Hash{Object=>Object}, nil)

Raises:

  • (ArgumentError)


155
156
157
158
159
160
161
162
163
# File 'lib/e_invoice_api/internal/util.rb', line 155

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

.coerce_integer(input) ⇒ Integer, Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • input (String, Integer)

Returns:

  • (Integer, Object)


122
123
124
# File 'lib/e_invoice_api/internal/util.rb', line 122

def coerce_integer(input)
  Integer(input, exception: false) || input
end

.decode_content(headers, stream:, suppress_error: false) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Assumes each chunk in stream has Encoding::BINARY.

Parameters:

  • headers (Hash{String=>String})
  • stream (Enumerable<String>)
  • suppress_error (Boolean) (defaults to: false)

Returns:

  • (Object)

Raises:

  • (JSON::ParserError)


701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
# File 'lib/e_invoice_api/internal/util.rb', line 701

def decode_content(headers, stream:, suppress_error: false)
  case (content_type = headers["content-type"])
  in EInvoiceAPI::Internal::Util::JSON_CONTENT
    return nil if (json = stream.to_a.join).empty?

    begin
      JSON.parse(json, symbolize_names: true)
    rescue JSON::ParserError => e
      raise e unless suppress_error
      json
    end
  in EInvoiceAPI::Internal::Util::JSONL_CONTENT
    lines = decode_lines(stream)
    chain_fused(lines) do |y|
      lines.each do
        next if _1.empty?

        y << JSON.parse(_1, symbolize_names: true)
      end
    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

.decode_lines(enum) ⇒ Enumerable<String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Assumes Strings have been forced into having Encoding::BINARY.

This decoder is responsible for reassembling lines split across multiple fragments.

Parameters:

  • enum (Enumerable<String>)

Returns:

  • (Enumerable<String>)


800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
# File 'lib/e_invoice_api/internal/util.rb', line 800

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

.decode_query(query) ⇒ Hash{String=>Array<String>}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • query (String, nil)

Returns:

  • (Hash{String=>Array<String>})


288
289
290
# File 'lib/e_invoice_api/internal/util.rb', line 288

def decode_query(query)
  CGI.parse(query.to_s)
end

.decode_sse(lines) ⇒ Enumerable<Hash{Symbol=>Object}>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

html.spec.whatwg.org/multipage/server-sent-events.html#parsing-an-event-stream

Assumes that lines has been decoded with #decode_lines.

Parameters:

  • lines (Enumerable<String>)

Returns:

  • (Enumerable<Hash{Symbol=>Object}>)


838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
# File 'lib/e_invoice_api/internal/util.rb', line 838

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

.deep_merge(*values, sentinel: nil, concat: false) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Recursively merge one hash with another. If the values at a given key are not both hashes, just take the new value.

Parameters:

  • values (Array<Object>)
  • sentinel (Object, nil) (defaults to: nil)

    the value to return if no values are provided.

  • concat (Boolean) (defaults to: false)

    whether to merge sequences by concatenation.

Returns:

  • (Object)


197
198
199
200
201
202
203
204
205
206
# File 'lib/e_invoice_api/internal/util.rb', line 197

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

.dig(data, pick, &blk) ⇒ Object?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • data (Hash{Symbol=>Object}, Array<Object>, Object)
  • pick (Symbol, Integer, Array<Symbol, Integer>, Proc, nil)
  • blk (Proc, nil)

Returns:

  • (Object, nil)


215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/e_invoice_api/internal/util.rb', line 215

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

.encode_content(headers, body) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

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

Returns:

  • (Object)


646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
# File 'lib/e_invoice_api/internal/util.rb', line 646

def encode_content(headers, body)
  # rubocop:disable Style/CaseEquality
  # rubocop:disable Layout/LineLength
  content_type = headers["content-type"]
  case [content_type, body]
  in [EInvoiceAPI::Internal::Util::JSON_CONTENT, Hash | Array | -> { primitive?(_1) }]
    [headers, JSON.generate(body)]
  in [EInvoiceAPI::Internal::Util::JSONL_CONTENT, Enumerable] unless EInvoiceAPI::Internal::Type::FileInput === body
    [headers, body.lazy.map { JSON.generate(_1) }]
  in [%r{^multipart/form-data}, Hash | EInvoiceAPI::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 [_, EInvoiceAPI::FilePart]
    [headers, body.content]
  else
    [headers, body]
  end
  # rubocop:enable Layout/LineLength
  # rubocop:enable Style/CaseEquality
end

.encode_path(path) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • path (String, Integer)

Returns:

  • (String)


260
261
262
# File 'lib/e_invoice_api/internal/util.rb', line 260

def encode_path(path)
  path.to_s.gsub(EInvoiceAPI::Internal::Util::RFC_3986_NOT_PCHARS) { ERB::Util.url_encode(_1) }
end

.encode_query(query) ⇒ String?

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • query (Hash{String=>Array<String>, String, nil}, nil)

Returns:

  • (String, nil)


297
298
299
# File 'lib/e_invoice_api/internal/util.rb', line 297

def encode_query(query)
  query.to_h.empty? ? nil : URI.encode_www_form(query)
end

.encode_query_params(query) ⇒ Hash{Symbol=>Object}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • query (Hash{Symbol=>Object})

Returns:

  • (Hash{Symbol=>Object})


512
513
514
515
516
# File 'lib/e_invoice_api/internal/util.rb', line 512

def encode_query_params(query)
  out = {}
  query.each { write_query_param_element!(out, _1, _2) }
  out
end

.force_charset!(content_type, text:) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

www.iana.org/assignments/character-sets/character-sets.xhtml

Parameters:

  • content_type (String)
  • text (String)


678
679
680
681
682
683
684
685
686
687
688
689
# File 'lib/e_invoice_api/internal/util.rb', line 678

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

.fused_enum(enum, external: false, &close) ⇒ Enumerable<Object>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

doc.rust-lang.org/std/iter/trait.FusedIterator.html

Parameters:

  • enum (Enumerable<Object>)
  • external (Boolean) (defaults to: false)
  • close (Proc)

Returns:

  • (Enumerable<Object>)


742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
# File 'lib/e_invoice_api/internal/util.rb', line 742

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

.interpolate_path(path) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • path (String, Array<String>)

Returns:

  • (String)


269
270
271
272
273
274
275
276
277
278
279
# File 'lib/e_invoice_api/internal/util.rb', line 269

def interpolate_path(path)
  case path
  in String
    path
  in []
    ""
  in [String => p, *interpolations]
    encoded = interpolations.map { encode_path(_1) }
    format(p, *encoded)
  end
end

.join_parsed_uri(lhs, rhs) ⇒ URI::Generic

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • lhs (Hash{Symbol=>String, Integer, nil})

    .

    @option lhs [String, nil] :scheme

    @option lhs [String, nil] :host

    @option lhs [Integer, nil] :port

    @option lhs [String, nil] :path

    @option lhs [HashString=>Array<String>] :query

  • rhs (Hash{Symbol=>String, Integer, nil})

    .

    @option rhs [String, nil] :scheme

    @option rhs [String, nil] :host

    @option rhs [Integer, nil] :port

    @option rhs [String, nil] :path

    @option rhs [HashString=>Array<String>] :query

Returns:

  • (URI::Generic)


359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/e_invoice_api/internal/util.rb', line 359

def join_parsed_uri(lhs, rhs)
  base_path, base_query = lhs.fetch_values(:path, :query)
  slashed = base_path.end_with?("/") ? base_path : "#{base_path}/"

  merged = {**parse_uri(rhs.fetch(:path)), **rhs.except(:path, :query)}
  parsed_path, parsed_query = merged.fetch_values(:path, :query)
  override = URI::Generic.build(**merged.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

.monotonic_secsFloat

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Float)


10
# File 'lib/e_invoice_api/internal/util.rb', line 10

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

.normalized_headers(*headers) ⇒ Hash{String=>String}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • headers (Hash{String=>String, Integer, Array<String, Integer, nil>, nil})

Returns:

  • (Hash{String=>String})


386
387
388
389
390
391
392
393
394
395
396
397
# File 'lib/e_invoice_api/internal/util.rb', line 386

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

.osString

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (String)


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/e_invoice_api/internal/util.rb', line 51

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

.parse_uri(url) ⇒ Hash{Symbol=>String, Integer, nil}

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • url (URI::Generic, String)

Returns:

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


308
309
310
311
# File 'lib/e_invoice_api/internal/util.rb', line 308

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

.primitive?(input) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • input (Object)

Returns:

  • (Boolean)


77
78
79
80
81
82
83
84
# File 'lib/e_invoice_api/internal/util.rb', line 77

def primitive?(input)
  case input
  in true | false | Numeric | Symbol | String
    true
  else
    false
  end
end

.unparse_uri(parsed) ⇒ URI::Generic

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • parsed (Hash{Symbol=>String, Integer, nil})

    .

    @option parsed [String, nil] :scheme

    @option parsed [String, nil] :host

    @option parsed [Integer, nil] :port

    @option parsed [String, nil] :path

    @option parsed [HashString=>Array<String>] :query

Returns:

  • (URI::Generic)


328
329
330
# File 'lib/e_invoice_api/internal/util.rb', line 328

def unparse_uri(parsed)
  URI::Generic.build(**parsed, query: encode_query(parsed.fetch(:query)))
end

.uri_origin(uri) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • uri (URI::Generic)

Returns:

  • (String)


251
252
253
# File 'lib/e_invoice_api/internal/util.rb', line 251

def uri_origin(uri)
  "#{uri.scheme}://#{uri.host}#{":#{uri.port}" unless uri.port == uri.default_port}"
end

.walk_namespaces(ns) ⇒ Enumerable<Module, Class>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • ns (Module, Class)

Returns:

  • (Enumerable<Module, Class>)


17
18
19
20
21
22
23
24
25
26
27
# File 'lib/e_invoice_api/internal/util.rb', line 17

def self.walk_namespaces(ns)
  ns.constants(false).lazy.flat_map do
    case (c = ns.const_get(_1, false))
    in Module | Class
      walk_namespaces(c)
    else
      []
    end
  end
    .chain([ns])
end

.writable_enum(&blk) {|| ... } ⇒ Enumerable<String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Parameters:

  • blk (Proc)

Yield Parameters:

  • (Enumerator::Yielder)

Returns:

  • (Enumerable<String>)


489
490
491
492
493
494
495
496
497
498
# File 'lib/e_invoice_api/internal/util.rb', line 489

def writable_enum(&blk)
  Enumerator.new do |y|
    y.define_singleton_method(:write) do
      self << _1.dup
      _1.bytesize
    end

    blk.call(y)
  end
end