use super::{
Frame, MAX_FRAME_BYTES, ProtocolError, READ_CHUNK_BYTES, SdkError, TcpStream, ToString, Vec,
format,
};
use alloc::vec;
use liminal::protocol::{decode, encode, encoded_len};
use std::io::{Read, Write};
pub(super) fn next_frame(stream: &mut TcpStream, buffer: &mut Vec<u8>) -> Result<Frame, SdkError> {
loop {
match decode(buffer) {
Ok((frame, consumed)) => {
buffer.drain(..consumed);
return Ok(frame);
}
Err(
ProtocolError::IncompleteHeader { .. } | ProtocolError::TruncatedPayload { .. },
) => match fill_buffer(stream, buffer)? {
FillOutcome::Read => {}
FillOutcome::TimedOut => {
return Err(SdkError::Connection {
description: "the subscription reader's steady-state socket reported a \
read deadline it should not carry"
.to_string(),
});
}
},
Err(error) => return Err(protocol_error(&error)),
}
}
}
pub(super) fn fill_buffer(
stream: &mut TcpStream,
buffer: &mut Vec<u8>,
) -> Result<FillOutcome, SdkError> {
if buffer.len() > MAX_FRAME_BYTES {
return Err(SdkError::Protocol {
description: format!(
"subscription frame exceeded {MAX_FRAME_BYTES} bytes without a complete frame"
),
});
}
let mut chunk = [0_u8; READ_CHUNK_BYTES];
match stream.read(&mut chunk) {
Ok(0) => Err(SdkError::Connection {
description: "server closed the subscription connection".to_string(),
}),
Ok(read) => {
let Some(received) = chunk.get(..read) else {
return Err(SdkError::Protocol {
description:
"subscription socket read reported more bytes than the buffer holds"
.to_string(),
});
};
buffer.extend_from_slice(received);
Ok(FillOutcome::Read)
}
Err(error)
if matches!(
error.kind(),
std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut
) =>
{
Ok(FillOutcome::TimedOut)
}
Err(error) => Err(SdkError::Connection {
description: format!("failed to read from subscription connection: {error}"),
}),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum FillOutcome {
Read,
TimedOut,
}
pub(super) fn write_frame(stream: &mut TcpStream, frame: &Frame) -> Result<(), SdkError> {
let len = encoded_len(frame).map_err(|error| protocol_error(&error))?;
let mut bytes = vec![0_u8; len];
let written = encode(frame, &mut bytes).map_err(|error| protocol_error(&error))?;
let encoded = bytes.get(..written).ok_or_else(|| SdkError::Protocol {
description: "subscription wire encoder reported an invalid byte count".to_string(),
})?;
stream
.write_all(encoded)
.map_err(|source| SdkError::Connection {
description: format!("failed to write subscription frame: {source}"),
})?;
stream.flush().map_err(|source| SdkError::Connection {
description: format!("failed to flush subscription frame: {source}"),
})
}
pub(super) fn protocol_error(error: &ProtocolError) -> SdkError {
SdkError::Protocol {
description: format!("subscription wire codec error: {error}"),
}
}