haematite-cli 0.21.0

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
//! Length-prefixed frames shared by local storage clients and the server.
//!
//! Four-byte big-endian lengths match Lodestone's existing port transport.
//! An oversize or cut frame ends the connection; its tail is never interpreted
//! as another request. Read/write deadlines belong to the connected transport.

use std::io::{self, Read, Write};
use std::num::NonZeroU32;

/// Read one frame, returning `None` only for EOF between complete frames.
///
/// # Errors
///
/// Propagates any error from `read_exact` or `try_reserve_exact`; an input that
/// ends inside a frame is `UnexpectedEof` naming the cut.
pub fn read(input: &mut impl Read, limit: NonZeroU32) -> io::Result<Option<Vec<u8>>> {
    let mut header = [0_u8; 4];
    loop {
        match input.read(&mut header[..1]) {
            Ok(0) => return Ok(None),
            Ok(_) => break,
            Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
            Err(error) => return Err(error),
        }
    }
    exact(input, &mut header[1..], "header")?;
    let length = u32::from_be_bytes(header);
    if length == 0 || length > limit.get() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("frame length {length} is outside 1..={limit}"),
        ));
    }
    let size = usize::try_from(length).map_err(io::Error::other)?;
    let mut body = Vec::new();
    body.try_reserve_exact(size).map_err(io::Error::other)?;
    body.resize(size, 0);
    exact(input, &mut body, "body")?;
    Ok(Some(body))
}

/// Fill `bytes`, naming an input that ends partway as a cut frame.
fn exact(input: &mut impl Read, bytes: &mut [u8], part: &str) -> io::Result<()> {
    input.read_exact(bytes).map_err(|error| {
        if error.kind() == io::ErrorKind::UnexpectedEof {
            io::Error::new(
                io::ErrorKind::UnexpectedEof,
                format!("frame cut: the input ended inside its {part}"),
            )
        } else {
            error
        }
    })
}

/// Write and flush one bounded frame. Refusals occur before writing the header.
///
/// # Errors
///
/// Propagates any error from `write_all`.
pub fn write(output: &mut impl Write, body: &[u8], limit: NonZeroU32) -> io::Result<()> {
    let length = u32::try_from(body.len()).map_err(io::Error::other)?;
    if length == 0 || length > limit.get() {
        return Err(io::Error::new(
            io::ErrorKind::InvalidData,
            format!("frame length {length} is outside 1..={limit}"),
        ));
    }
    output.write_all(&length.to_be_bytes())?;
    output.write_all(body)?;
    output.flush()
}