Module: Kobako::Wire::Envelope

Defined in:
lib/kobako/wire/envelope.rb,
lib/kobako/wire/envelope/payloads.rb

Overview

Outcome-path envelopes (SPEC.md Outcome Envelope): Result and Panic value objects plus the tagged Outcome wrapper that frames them on the wire. The RPC-path counterparts (Request / Response) live in the parent envelope.rb file.

Defined Under Namespace

Classes: Outcome, Panic, Request, Response, Result

Constant Summary collapse

OUTCOME_TAG_RESULT =

First byte of the OUTCOME_BUFFER for a Result envelope.

0x01
OUTCOME_TAG_PANIC =

First byte of the OUTCOME_BUFFER for a Panic envelope.

0x02
STATUS_OK =

Response variant marker for the success branch.

0
STATUS_ERROR =

Response variant marker for the error branch.

1

Class Method Summary collapse

Class Method Details

.decode_outcome(bytes) ⇒ Object

Raises:



126
127
128
129
130
131
132
133
# File 'lib/kobako/wire/envelope/payloads.rb', line 126

def self.decode_outcome(bytes)
  bytes = bytes.b
  raise Codec::InvalidType, "Outcome bytes must not be empty" if bytes.empty?

  tag = bytes.getbyte(0)
  body = bytes.byteslice(1, bytes.bytesize - 1)
  Outcome.new(decode_outcome_payload(tag, body))
end

.decode_panic(bytes) ⇒ Object

Raises:



76
77
78
79
80
81
82
83
84
85
86
# File 'lib/kobako/wire/envelope/payloads.rb', line 76

def self.decode_panic(bytes)
  map = Codec::Decoder.decode(bytes)
  raise Codec::InvalidType, "Panic envelope must be a map, got #{map.class}" unless map.is_a?(Hash)

  Codec.translate_value_object_error do
    Panic.new(
      origin: map["origin"], klass: map["class"], message: map["message"],
      backtrace: map["backtrace"] || [], details: map["details"]
    )
  end
end

.decode_request(bytes) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
# File 'lib/kobako/wire/envelope.rb', line 83

def self.decode_request(bytes)
  arr = Codec::Decoder.decode(bytes)
  unless arr.is_a?(Array) && arr.length == 4
    raise Codec::InvalidType, "Request must be a 4-element array, got #{arr.inspect}"
  end

  target, method_name, args, kwargs = arr
  Codec.translate_value_object_error do
    Request.new(target: target, method: method_name, args: args, kwargs: kwargs)
  end
end

.decode_response(bytes) ⇒ Object



134
135
136
137
138
139
140
141
142
# File 'lib/kobako/wire/envelope.rb', line 134

def self.decode_response(bytes)
  arr = Codec::Decoder.decode(bytes)
  unless arr.is_a?(Array) && arr.length == 2
    raise Codec::InvalidType, "Response must be a 2-element array, got #{arr.inspect}"
  end

  status, payload = arr
  Codec.translate_value_object_error { Response.new(status: status, payload: payload) }
end

.decode_result(bytes) ⇒ Object



26
27
28
29
30
31
32
33
# File 'lib/kobako/wire/envelope/payloads.rb', line 26

def self.decode_result(bytes)
  arr = Codec::Decoder.decode(bytes)
  unless arr.is_a?(Array) && arr.length == 1
    raise Codec::InvalidType, "Result envelope must be a 1-element array, got #{arr.inspect}"
  end

  Result.new(arr[0])
end

.encode_outcome(outcome) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/kobako/wire/envelope/payloads.rb', line 110

def self.encode_outcome(outcome)
  tag, body = encode_outcome_payload(outcome.payload)
  out = String.new(encoding: Encoding::ASCII_8BIT)
  out << [tag].pack("C")
  out << body
  out
end

.encode_panic(panic) ⇒ Object



59
60
61
# File 'lib/kobako/wire/envelope/payloads.rb', line 59

def self.encode_panic(panic)
  Codec::Encoder.encode(panic_map(panic))
end

.encode_request(request) ⇒ Object

Encode a Request to bytes. The Value Object’s own invariants are the contract; this method does not re-check the shape.



79
80
81
# File 'lib/kobako/wire/envelope.rb', line 79

def self.encode_request(request)
  Codec::Encoder.encode([request.target, request.method_name, request.args, request.kwargs])
end

.encode_response(response) ⇒ Object



130
131
132
# File 'lib/kobako/wire/envelope.rb', line 130

def self.encode_response(response)
  Codec::Encoder.encode([response.status, response.payload])
end

.encode_result(value) ⇒ Object



22
23
24
# File 'lib/kobako/wire/envelope/payloads.rb', line 22

def self.encode_result(value)
  Codec::Encoder.encode([value])
end