//! Buffered newline framing shared by stdin and endpoint connections. #[cfg(any(target_os = "wasi", test))] #[derive(Default)] pub(crate) struct Lines { bytes: Vec, scanned: usize, } #[cfg(any(target_os = "wasi", test))] impl Lines { fn frame(&mut self) -> Option> { if let Some(end) = self.bytes[self.scanned..].iter().position(|b| *b == b'\n') { let end = self.scanned + end; self.scanned = 0; return Some(self.bytes.drain(..=end).collect()); } self.scanned = self.bytes.len(); None } #[cfg(target_os = "wasi")] pub(crate) async fn next( &mut self, input: &mut impl wstd::io::AsyncRead, ) -> std::io::Result>> { loop { if let Some(line) = self.frame() { return Ok(Some(line)); } let mut buffer = [0; 8192]; let count = input.read(&mut buffer).await?; if count == 0 { self.scanned = 0; return Ok(if self.bytes.is_empty() { None } else { Some(std::mem::take(&mut self.bytes)) }); } self.bytes.extend_from_slice(&buffer[..count]); } } #[cfg(target_os = "wasi")] pub(crate) async fn rpc( &mut self, input: &mut impl wstd::io::AsyncRead, ) -> std::io::Result> { while let Some(bytes) = self.next(input).await? { if let Ok(value) = serde_json::from_slice::(&bytes) && value.is_object() && value["jsonrpc"] == "2.0" { return Ok(Some(value)); } } Ok(None) } } #[cfg(test)] mod tests { #[test] fn incomplete_frames_retain_the_scanned_length() { let mut lines = super::Lines::default(); lines.bytes.extend_from_slice(b"first"); assert!(lines.frame().is_none()); assert_eq!(lines.scanned, 5); lines.bytes.extend_from_slice(b"\nsecond\n"); assert_eq!(lines.frame().unwrap(), b"first\n"); assert_eq!(lines.frame().unwrap(), b"second\n"); assert!(lines.frame().is_none()); } }