use alloc::vec::Vec;
use std::io::{self, BufRead, Read};
use crate::binary_sink::BinarySink;
use crate::convert::Converter;
pub struct StreamConverter<R> {
input: R,
converter: Converter,
out_buf: Vec<u8>,
out_pos: usize,
line_buf: Vec<u8>,
done: bool,
}
impl<R: BufRead> StreamConverter<R> {
pub fn new(input: R) -> Self {
Self {
input,
converter: Converter::new(),
out_buf: Vec::new(),
out_pos: 0,
line_buf: Vec::new(),
done: false,
}
}
}
impl<R: BufRead> Read for StreamConverter<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
loop {
if self.out_pos < self.out_buf.len() {
let available = &self.out_buf[self.out_pos..];
let n = available.len().min(buf.len());
buf[..n].copy_from_slice(&available[..n]);
self.out_pos += n;
if self.out_pos == self.out_buf.len() {
self.out_buf.clear();
self.out_pos = 0;
}
return Ok(n);
}
if self.done {
return Ok(0);
}
self.line_buf.clear();
let n = self.input.read_until(b'\n', &mut self.line_buf)?;
if n == 0 {
self.done = true;
return Ok(0);
}
if !self.line_buf.ends_with(b"\n") {
self.line_buf.push(b'\n');
}
let mut sink = BinarySink::new(&mut self.out_buf);
self.converter
.feed(&self.line_buf, &mut sink)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, alloc::format!("{e}")))?;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::convert::convert_all;
use std::io::BufReader;
#[test]
fn stream_matches_direct() {
let text_dxf = b" 0\nSECTION\n 2\nHEADER\n 0\nENDSEC\n 0\nEOF\n";
let reader = BufReader::new(&text_dxf[..]);
let mut stream = StreamConverter::new(reader);
let mut stream_out = Vec::new();
stream.read_to_end(&mut stream_out).unwrap();
let mut direct_out = Vec::new();
let mut sink = BinarySink::new(&mut direct_out);
convert_all(text_dxf, &mut sink).unwrap();
assert_eq!(stream_out, direct_out);
}
#[test]
fn stream_with_numeric_types() {
let text_dxf = b" 10\n1.5\n 70\n42\n290\n1\n";
let reader = BufReader::new(&text_dxf[..]);
let mut stream = StreamConverter::new(reader);
let mut stream_out = Vec::new();
stream.read_to_end(&mut stream_out).unwrap();
let mut direct_out = Vec::new();
let mut sink = BinarySink::new(&mut direct_out);
convert_all(text_dxf, &mut sink).unwrap();
assert_eq!(stream_out, direct_out);
}
#[test]
fn stream_small_reads() {
let text_dxf = b" 0\nSECTION\n 0\nEOF\n";
let reader = BufReader::new(&text_dxf[..]);
let mut stream = StreamConverter::new(reader);
let mut out = Vec::new();
let mut byte = [0u8; 1];
loop {
match stream.read(&mut byte) {
Ok(0) => break,
Ok(_) => out.push(byte[0]),
Err(e) => panic!("{e}"),
}
}
let mut direct_out = Vec::new();
let mut sink = BinarySink::new(&mut direct_out);
convert_all(text_dxf, &mut sink).unwrap();
assert_eq!(out, direct_out);
}
#[test]
fn stream_no_trailing_newline() {
let text_dxf = b" 0\nEOF";
let reader = BufReader::new(&text_dxf[..]);
let mut stream = StreamConverter::new(reader);
let mut stream_out = Vec::new();
stream.read_to_end(&mut stream_out).unwrap();
let mut direct_out = Vec::new();
let mut sink = BinarySink::new(&mut direct_out);
convert_all(text_dxf, &mut sink).unwrap();
assert_eq!(stream_out, direct_out);
}
}