#![no_std] //! Bounded echo formatting with resumable output ownership. use turkos_os_user_runtime_io::io::{self, Outcome, Writer}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum EncodeError { Full, Nul } pub struct Echo { bytes: [u8; N], len: usize, sent: usize, } impl Echo { pub fn encode(words: &[&str], newline: bool) -> Result { let mut result = Self {bytes: [0; N], len: 0, sent: 0}; for (index, word) in words.iter().enumerate() { if word.as_bytes().contains(&0) { return Err(EncodeError::Nul); } if index != 0 { result.append(b" ")?; } result.append(word.as_bytes())?; } if newline { result.append(b"\n")?; } Ok(result) } fn append(&mut self, bytes: &[u8]) -> Result<(), EncodeError> { if bytes.len() > N - self.len { return Err(EncodeError::Full); } self.bytes[self.len..self.len + bytes.len()].copy_from_slice(bytes); self.len += bytes.len(); Ok(()) } pub fn as_bytes(&self) -> &[u8] { &self.bytes[..self.len] } pub const fn sent(&self) -> usize { self.sent } pub fn remaining(&self) -> &[u8] { &self.bytes[self.sent..self.len] } pub const fn complete(&self) -> bool { self.sent == self.len } pub fn emit(&mut self, writer: &mut W, max_calls: usize) -> Result { let result = io::write_all(writer, self.remaining(), max_calls); let credited = match &result { Ok(outcome) => outcome.bytes, Err(io::Error::BudgetExceeded(outcome) | io::Error::Stalled(outcome) | io::Error::EndOfInput(outcome) | io::Error::InvalidCount(outcome) | io::Error::Kernel(_, outcome) | io::Error::Wire(outcome)) => outcome.bytes, }; self.sent += credited; result } }