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
pub(super) async fn eof() -> anyhow::Result<()> {
    use std::os::unix::fs::OpenOptionsExt as _;
    let flags = i32::try_from(rustix::fs::OFlags::NONBLOCK.bits())?;
    let file = std::fs::OpenOptions::new()
        .read(true)
        .custom_flags(flags)
        .open("/dev/stdin")?;
    let input = tokio::io::unix::AsyncFd::new(file)?;
    loop {
        let mut ready = input.readable().await?;
        match ready.try_io(|input| read_once(input.get_ref())) {
            Ok(Ok(0)) => return Ok(()),
            Ok(Ok(_)) | Err(_) => {}
            Ok(Err(error)) => {
                return Err(anyhow::anyhow!("Lys host input failed: {}", error.kind()));
            }
        }
    }
}

fn read_once(file: &impl std::os::fd::AsFd) -> std::io::Result<usize> {
    rustix::io::read(file, &mut [0_u8; 8192]).map_err(std::io::Error::from)
}

#[cfg(test)]
#[path = "input_tests.rs"]
mod tests;