///|
/// The NSQ V2 frame header is a 32-bit, big-endian length followed by a type.
pub let max_frame_size : Int = 1_048_576
///|
pub enum Frame {
Response(Bytes)
Error(Bytes)
Message(Bytes)
} derive(Debug, Eq)
///|
pub extend Frame with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub extend Frame with Eq::{equal, not_equal}
///|
pub suberror ProtocolError {
InvalidFrameLength(Int)
UnknownFrameType(Int)
TruncatedFrame(Int)
InvalidCommand(String)
TruncatedMessage(Int)
ServerError(String)
UnexpectedFrame(String)
InvalidOption(String)
InvalidState(String)
} derive(Debug)
///|
pub extend ProtocolError with @moonbitlang/core/debug.Debug::{to_repr}
///|
pub fn decode_u32(data : Bytes, offset : Int) -> Int {
(data[offset].to_int() << 24) |
(data[offset + 1].to_int() << 16) |
(data[offset + 2].to_int() << 8) |
data[offset + 3].to_int()
}
///|
pub fn encode_u32(value : Int) -> Bytes {
Bytes::from_array([
((value >> 24) & 255).to_byte(),
((value >> 16) & 255).to_byte(),
((value >> 8) & 255).to_byte(),
(value & 255).to_byte(),
])
}
///|
/// Encodes one NSQ V2 frame. `body` does not include the four-byte type field.
pub fn encode_frame(kind : Int, body : Bytes) -> Bytes raise ProtocolError {
let size = body.length() + 4
if size > max_frame_size {
raise InvalidFrameLength(size)
}
let output = @buffer.Buffer(size_hint=size + 4)
output.write_bytes(encode_u32(size))
output.write_bytes(encode_u32(kind))
output.write_bytes(body)
output.to_bytes()
}
///|
/// Decodes a complete NSQ V2 frame, including its length prefix.
pub fn decode_frame(data : Bytes) -> Frame raise ProtocolError {
if data.length() < 4 {
raise TruncatedFrame(4)
}
let size = decode_u32(data, 0)
if size < 4 || size > max_frame_size {
raise InvalidFrameLength(size)
}
if data.length() != size + 4 {
raise TruncatedFrame(size + 4)
}
let kind = decode_u32(data, 4)
let body = data[8:].to_owned()
match kind {
0 => Response(body)
1 => Error(body)
2 => Message(body)
_ => raise UnknownFrameType(kind)
}
}