#![deny(missing_docs, unsafe_code)]
use std::{path::Path, sync::Arc};
use gix_object::bstr::BString;
pub struct Stream {
read: utils::Read,
err: SharedErrorSlot,
extra_entries: Option<std::sync::mpsc::Sender<AdditionalEntry>>,
path_buf: Option<BString>,
buf: Vec<u8>,
pos: usize,
filled: usize,
}
pub mod entry;
pub(crate) mod protocol;
mod from_tree;
pub use from_tree::from_tree;
pub(crate) type SharedErrorSlot = Arc<parking_lot::Mutex<Option<entry::Error>>>;
pub struct Entry<'a> {
pub mode: gix_object::tree::EntryMode,
pub id: gix_hash::ObjectId,
parent: &'a mut Stream,
path_buf: Option<BString>,
remaining: Option<usize>,
}
pub struct AdditionalEntry {
pub id: gix_hash::ObjectId,
pub mode: gix_object::tree::EntryMode,
pub relative_path: BString,
pub source: entry::Source,
}
impl Stream {
pub fn into_read(self) -> impl std::io::Read {
self.read
}
pub fn as_read_mut(&mut self) -> &mut impl std::io::Read {
self.extra_entries.take();
&mut self.read
}
pub fn from_read(read: impl std::io::Read + 'static) -> Self {
Self {
read: utils::Read::Unknown(Box::new(read)),
extra_entries: None,
path_buf: Some(Vec::with_capacity(1024).into()),
err: Default::default(),
buf: std::iter::repeat_n(0, u16::MAX as usize).collect(),
pos: 0,
filled: 0,
}
}
}
impl Stream {
pub fn add_entry(&mut self, entry: AdditionalEntry) -> &mut Self {
self.extra_entries
.as_ref()
.expect("BUG: must not add entries after the start of entries traversal")
.send(entry)
.expect("Failure is impossible as thread blocks on the receiving end");
self
}
pub fn add_entry_from_path(
&mut self,
root: &Path,
path: &Path,
object_hash: gix_hash::Kind,
) -> std::io::Result<&mut Self> {
let rela_path = path.strip_prefix(root).map_err(std::io::Error::other)?;
let meta = path.symlink_metadata()?;
let relative_path = gix_path::to_unix_separators_on_windows(gix_path::into_bstr(rela_path)).into_owned();
let id = object_hash.null();
let entry = if meta.is_symlink() {
let content = std::fs::read_link(path)?;
let content = gix_path::into_bstr(content).into_owned();
AdditionalEntry {
id,
mode: gix_object::tree::EntryKind::Link.into(),
relative_path,
source: entry::Source::Memory(content.into()),
}
} else if meta.is_dir() {
AdditionalEntry {
id,
mode: gix_object::tree::EntryKind::Tree.into(),
relative_path,
source: entry::Source::Null,
}
} else {
let mode = if gix_fs::is_executable(&meta) {
gix_object::tree::EntryKind::BlobExecutable
} else {
gix_object::tree::EntryKind::Blob
}
.into();
AdditionalEntry {
id,
mode,
relative_path,
source: entry::Source::Path(path.to_owned()),
}
};
Ok(self.add_entry(entry))
}
}
impl Stream {
pub(crate) fn new() -> (
Stream,
gix_features::io::pipe::Writer,
std::sync::mpsc::Receiver<AdditionalEntry>,
) {
let in_flight_writes = (2 + 1) * 32;
let (write, read) = gix_features::io::pipe::unidirectional(in_flight_writes);
let (tx_entries, rx_entries) = std::sync::mpsc::channel();
(
Stream {
read: utils::Read::Known(read),
extra_entries: Some(tx_entries),
path_buf: Some(Vec::with_capacity(1024).into()),
err: Default::default(),
buf: std::iter::repeat_n(0, u16::MAX as usize).collect(),
pos: 0,
filled: 0,
},
write,
rx_entries,
)
}
}
pub(crate) mod utils {
pub enum Read {
Known(gix_features::io::pipe::Reader),
Unknown(Box<dyn std::io::Read>),
}
impl std::io::Read for Read {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
match self {
Read::Known(r) => r.read(buf),
Read::Unknown(r) => r.read(buf),
}
}
}
}