use crate::{ borrowed::{Band, Text}, decode, MAX_DATA_LEN, MAX_LINE_LEN, }; use crate::{PacketLine, RemoteProgress}; use bstr::ByteSlice; use git_features::{progress, progress::Progress}; use std::io; /// Read pack lines one after another, without consuming more than needed from the underlying /// `Read`. `Flush` lines cause the reader to stop producing lines forever, leaving `Read` at the /// start of whatever comes next. pub struct Reader { pub inner: T, buf: Vec, delimiter: PacketLine<'static>, is_done: bool, } impl Reader where T: io::Read, { pub fn new(inner: T, delimiter: impl Into>>) -> Self { Reader { inner, buf: vec![0; MAX_LINE_LEN], delimiter: delimiter.into().unwrap_or(PacketLine::Flush), is_done: false, } } pub fn reset(&mut self) { debug_assert!(self.is_done, "reset is only effective if we are actually done"); self.is_done = false; } fn read_line_inner<'a>(reader: &mut T, buf: &'a mut Vec) -> io::Result, decode::Error>> { let (hex_bytes, data_bytes) = buf.split_at_mut(4); reader.read_exact(hex_bytes)?; let num_data_bytes = match decode::hex_prefix(hex_bytes) { Ok(decode::PacketLineOrWantedSize::Line(line)) => return Ok(Ok(line)), Ok(decode::PacketLineOrWantedSize::Wanted(additional_bytes)) => additional_bytes as usize, Err(err) => return Ok(Err(err)), }; let (data_bytes, _) = data_bytes.split_at_mut(num_data_bytes); reader.read_exact(data_bytes)?; match decode::to_data_line(data_bytes) { Ok(line) => Ok(Ok(line)), Err(err) => Ok(Err(err)), } } pub fn read_line(&mut self) -> Option>> { if self.is_done { return None; } match Self::read_line_inner(&mut self.inner, &mut self.buf) { Ok(Ok(line)) if line == self.delimiter => { self.is_done = true; None } res => Some(res), } } pub fn as_read_with_sidebands( &mut self, progress: P, parse_progress: fn(&[u8]) -> Option, ) -> ReadWithProgress { ReadWithProgress::with_progress(self, progress, parse_progress) } pub fn as_read(&mut self) -> ReadWithProgress { ReadWithProgress::new(self) } } type ProgressAndParser

= (P, fn(&[u8]) -> Option); pub struct ReadWithProgress<'a, T, P> { parent: &'a mut Reader, progress_and_parse: Option>, buf: Vec, pos: usize, cap: usize, } impl<'a, T> ReadWithProgress<'a, T, progress::Discard> where T: io::Read, { fn new(parent: &'a mut Reader) -> Self { ReadWithProgress { parent, progress_and_parse: None, buf: vec![0; MAX_DATA_LEN], pos: 0, cap: 0, } } } impl<'a, T, P> ReadWithProgress<'a, T, P> where T: io::Read, P: Progress, { fn with_progress( parent: &'a mut Reader, progress: P, parse_progress: fn(&[u8]) -> Option, ) -> Self { ReadWithProgress { parent, progress_and_parse: Some((progress, parse_progress)), buf: vec![0; MAX_DATA_LEN], pos: 0, cap: 0, } } } impl<'a, T, P> io::BufRead for ReadWithProgress<'a, T, P> where T: io::Read, P: Progress, { fn fill_buf(&mut self) -> io::Result<&[u8]> { use io::Read; if self.pos >= self.cap { debug_assert!(self.pos == self.cap); self.cap = loop { let line = match self.parent.read_line() { Some(line) => line?.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?, None => break 0, }; match self.progress_and_parse.as_mut() { Some((progress, parse_progress)) => { let mut band = line .decode_band() .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?; fn progress_name(current: Option, action: &[u8]) -> String { match current { Some(current) => format!("{}: {}", current, action.as_bstr()), None => action.as_bstr().to_string(), } } match band { Band::Data(ref mut d) => break d.read(&mut self.buf)?, Band::Progress(d) => { let text = Text::from(d).0; match (parse_progress)(text) { Some(RemoteProgress { action, percent: _, step, max, }) => { progress.set_name(progress_name(progress.name(), action)); progress.init(max, git_features::progress::count("objects")); if let Some(step) = step { progress.set(step); } } None => progress.set_name(progress_name(progress.name(), text)), }; } Band::Error(d) => progress.fail(progress_name(None, Text::from(d).0)), }; } None => { break match line.as_slice() { Some(ref mut d) => d.read(&mut self.buf)?, None => { return Err(io::Error::new( io::ErrorKind::UnexpectedEof, "encountered non-data line in a data-line only context", )) } } } } }; self.pos = 0; } Ok(&self.buf[self.pos..self.cap]) } fn consume(&mut self, amt: usize) { self.pos = std::cmp::min(self.pos + amt, self.cap); } } impl<'a, T, P> io::Read for ReadWithProgress<'a, T, P> where T: io::Read, P: Progress, { fn read(&mut self, buf: &mut [u8]) -> io::Result { use std::io::BufRead; let nread = { let mut rem = self.fill_buf()?; rem.read(buf)? }; self.consume(nread); Ok(nread) } }