150 lines · 4.7 KB · Rust
RawDownload
use std::{    io::{ErrorKind, Read},    path::PathBuf,}; use gix_error::ErrorExt;use gix_object::bstr::BStr; use crate::{Entry, Stream, protocol}; /// The error returned by [`next_entry()`][Stream::next_entry()].pub type Error = gix_error::Exn<gix_error::Message>; impl Stream {    /// Access the next entry of the stream or `None` if there is nothing more to read.    pub fn next_entry(&mut self) -> Result<Option<Entry<'_>>, Error> {        assert!(            self.path_buf.is_some(),            "BUG: must consume and drop entry before getting the next one"        );        self.extra_entries.take();        let res = protocol::read_entry_info(            &mut self.read,            self.path_buf.as_mut().expect("set while producing an entry"),        );        match res {            Ok((remaining, mode, id)) => {                if let Some(err) = self.err.lock().take() {                    return Err(err);                }                Ok(Some(Entry {                    path_buf: self.path_buf.take(),                    parent: self,                    id,                    mode,                    remaining,                }))            }            Err(err) => {                if let Some(err) = self.err.lock().take() {                    return Err(err);                }                // unexpected EOF means the other side dropped. We handled potential errors already.                if err.kind() == ErrorKind::UnexpectedEof {                    return Ok(None);                }                Err(err.and_raise(gix_error::message("Could not read stream entry")))            }        }    }} /// The source of an additional entrypub enum Source {    /// There is no content, typically the case with directories which are always considered empty.    Null,    /// Read from the file at the given path.    Path(PathBuf),    /// Read from memory.    Memory(Vec<u8>),} impl Source {    pub(crate) fn len(&self) -> Option<usize> {        match self {            Source::Null => Some(0),            Source::Path(_) => None,            Source::Memory(buf) => Some(buf.len()),        }    }} impl std::fmt::Debug for Entry<'_> {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        f.debug_struct("Entry")            .field("path_buf", &self.relative_path())            .field("mode", &self.mode)            .field("id", &self.id)            .field("remaining", &self.remaining)            .finish()    }} impl Entry<'_> {    /// Return the path of this entry as slash-separated path relative to the repository.    pub fn relative_path(&self) -> &BStr {        self.path_buf.as_ref().expect("always set during our lifetime").as_ref()    }     /// The amount of bytes that remain to be read, or `None` if it's fully streamed.    ///    /// This equals the length of the entry in bytes right before reading it.    pub fn bytes_remaining(&self) -> Option<usize> {        self.remaining    }} impl Drop for Entry<'_> {    fn drop(&mut self) {        if self.remaining == Some(0) {            self.parent.path_buf = self.path_buf.take();        }    }} impl Entry<'_> {    fn fill_buf(&mut self) -> std::io::Result<&[u8]> {        if self.parent.pos >= self.parent.filled {            let mut u16_buf = [0; 2];            self.parent.read.read_exact(&mut u16_buf)?;            let nb = u16::from_le_bytes(u16_buf) as usize;             if nb != 0 {                self.parent.read.read_exact(&mut self.parent.buf[..nb])?;            }            self.parent.filled = nb;            self.parent.pos = 0;        }        Ok(&self.parent.buf[self.parent.pos..self.parent.filled])    }} impl std::io::Read for Entry<'_> {    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {        let buf_len = buf.len();        if let Some(err) = self.parent.err.lock().take() {            return Err(std::io::Error::other(err.into_error()));        }        let bytes_read = match self.remaining.as_mut() {            None => {                // We expect a zero-read to indicate the end of stream, which is the default way of streams to end.                // In our case though, it requires sending an extra zero-write, so we avoid that usually.                let input = self.fill_buf()?;                let nb = input.len().min(buf.len());                buf[..nb].copy_from_slice(&input[..nb]);                self.parent.pos += nb;                nb            }            Some(remaining) => {                let bytes_read = self.parent.read.read(&mut buf[..buf_len.min(*remaining)])?;                *remaining -= bytes_read;                bytes_read            }        };        if bytes_read == 0 {            self.remaining = Some(0);        }        Ok(bytes_read)    }}