1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
use std::{
io::{ErrorKind, Read},
path::PathBuf,
};
use gix_object::bstr::{BStr, BString};
use crate::{protocol, Entry, Stream};
/// The error returned by [`next_entry()`][Stream::next_entry()].
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("Could not find a tree's leaf, typically a blob")]
Find(#[from] gix_object::find::existing::Error),
#[error("Could not find a tree to traverse")]
FindTree(#[from] gix_object::find::existing_iter::Error),
#[error("Could not query attributes for path \"{path}\"")]
Attributes {
path: BString,
source: Box<dyn std::error::Error + Send + Sync + 'static>,
},
#[error(transparent)]
Traverse(#[from] gix_traverse::tree::breadthfirst::Error),
#[error(transparent)]
ConvertToWorktree(#[from] gix_filter::pipeline::convert::to_worktree::Error),
}
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.into())
}
}
}
}
/// The source of an additional entry
pub 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::new(ErrorKind::Other, err));
}
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)
}
}
|