Skip to main content

gix_worktree_stream/
entry.rs

1use std::{
2    io::{ErrorKind, Read},
3    path::PathBuf,
4};
5
6use gix_error::ErrorExt;
7use gix_object::bstr::BStr;
8
9use crate::{protocol, Entry, Stream};
10
11/// The error returned by [`next_entry()`][Stream::next_entry()].
12pub type Error = gix_error::Exn<gix_error::Message>;
13
14impl Stream {
15    /// Access the next entry of the stream or `None` if there is nothing more to read.
16    pub fn next_entry(&mut self) -> Result<Option<Entry<'_>>, Error> {
17        assert!(
18            self.path_buf.is_some(),
19            "BUG: must consume and drop entry before getting the next one"
20        );
21        self.extra_entries.take();
22        let res = protocol::read_entry_info(
23            &mut self.read,
24            self.path_buf.as_mut().expect("set while producing an entry"),
25        );
26        match res {
27            Ok((remaining, mode, id)) => {
28                if let Some(err) = self.err.lock().take() {
29                    return Err(err);
30                }
31                Ok(Some(Entry {
32                    path_buf: self.path_buf.take(),
33                    parent: self,
34                    id,
35                    mode,
36                    remaining,
37                }))
38            }
39            Err(err) => {
40                if let Some(err) = self.err.lock().take() {
41                    return Err(err);
42                }
43                // unexpected EOF means the other side dropped. We handled potential errors already.
44                if err.kind() == ErrorKind::UnexpectedEof {
45                    return Ok(None);
46                }
47                Err(err.and_raise(gix_error::message("Could not read stream entry")))
48            }
49        }
50    }
51}
52
53/// The source of an additional entry
54pub enum Source {
55    /// There is no content, typically the case with directories which are always considered empty.
56    Null,
57    /// Read from the file at the given path.
58    Path(PathBuf),
59    /// Read from memory.
60    Memory(Vec<u8>),
61}
62
63impl Source {
64    pub(crate) fn len(&self) -> Option<usize> {
65        match self {
66            Source::Null => Some(0),
67            Source::Path(_) => None,
68            Source::Memory(buf) => Some(buf.len()),
69        }
70    }
71}
72
73impl std::fmt::Debug for Entry<'_> {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        f.debug_struct("Entry")
76            .field("path_buf", &self.relative_path())
77            .field("mode", &self.mode)
78            .field("id", &self.id)
79            .field("remaining", &self.remaining)
80            .finish()
81    }
82}
83
84impl Entry<'_> {
85    /// Return the path of this entry as slash-separated path relative to the repository.
86    pub fn relative_path(&self) -> &BStr {
87        self.path_buf.as_ref().expect("always set during our lifetime").as_ref()
88    }
89
90    /// The amount of bytes that remain to be read, or `None` if it's fully streamed.
91    ///
92    /// This equals the length of the entry in bytes right before reading it.
93    pub fn bytes_remaining(&self) -> Option<usize> {
94        self.remaining
95    }
96}
97
98impl Drop for Entry<'_> {
99    fn drop(&mut self) {
100        if self.remaining == Some(0) {
101            self.parent.path_buf = self.path_buf.take();
102        }
103    }
104}
105
106impl Entry<'_> {
107    fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
108        if self.parent.pos >= self.parent.filled {
109            let mut u16_buf = [0; 2];
110            self.parent.read.read_exact(&mut u16_buf)?;
111            let nb = u16::from_le_bytes(u16_buf) as usize;
112
113            if nb != 0 {
114                self.parent.read.read_exact(&mut self.parent.buf[..nb])?;
115            }
116            self.parent.filled = nb;
117            self.parent.pos = 0;
118        }
119        Ok(&self.parent.buf[self.parent.pos..self.parent.filled])
120    }
121}
122
123impl std::io::Read for Entry<'_> {
124    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
125        let buf_len = buf.len();
126        if let Some(err) = self.parent.err.lock().take() {
127            return Err(std::io::Error::other(err.into_error()));
128        }
129        let bytes_read = match self.remaining.as_mut() {
130            None => {
131                // We expect a zero-read to indicate the end of stream, which is the default way of streams to end.
132                // In our case though, it requires sending an extra zero-write, so we avoid that usually.
133                let input = self.fill_buf()?;
134                let nb = input.len().min(buf.len());
135                buf[..nb].copy_from_slice(&input[..nb]);
136                self.parent.pos += nb;
137                nb
138            }
139            Some(remaining) => {
140                let bytes_read = self.parent.read.read(&mut buf[..buf_len.min(*remaining)])?;
141                *remaining -= bytes_read;
142                bytes_read
143            }
144        };
145        if bytes_read == 0 {
146            self.remaining = Some(0);
147        }
148        Ok(bytes_read)
149    }
150}