use crate::{hash, pack, pack::data::output};
use std::io::Write;
#[allow(missing_docs)]
#[derive(Debug, thiserror::Error)]
pub enum Error<E>
where
E: std::error::Error + 'static,
{
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Input(E),
}
pub struct EntriesToBytesIter<I, W> {
pub input: I,
output: hash::Write<W>,
header_info: Option<(pack::data::Version, u32)>,
entry_version: pack::data::Version,
is_done: bool,
}
impl<I, W, E> EntriesToBytesIter<I, W>
where
I: Iterator<Item = Result<Vec<output::Entry>, E>>,
W: std::io::Write,
E: std::error::Error + 'static,
{
pub fn new(input: I, output: W, num_entries: u32, version: pack::data::Version, hash_kind: git_hash::Kind) -> Self {
assert!(
matches!(version, pack::data::Version::V2),
"currently only pack version 2 can be written",
);
assert!(
matches!(hash_kind, git_hash::Kind::Sha1),
"currently only Sha1 is supported",
);
EntriesToBytesIter {
input,
output: hash::Write::new(output, hash_kind),
entry_version: version,
header_info: Some((version, num_entries)),
is_done: false,
}
}
pub fn into_write(self) -> W {
self.output.inner
}
fn next_inner(&mut self) -> Result<u64, Error<E>> {
let mut written = 0u64;
if let Some((version, num_entries)) = self.header_info.take() {
let header_bytes = pack::data::header::encode(version, num_entries);
self.output.write_all(&header_bytes[..])?;
written += header_bytes.len() as u64;
}
match self.input.next() {
Some(entries) => {
for entry in entries.map_err(Error::Input)? {
let header = entry.to_entry_header(self.entry_version, |_index_offset| {
unimplemented!("a way to calculate pack offsets from object index offsets")
});
written += header.to_write(entry.decompressed_size as u64, &mut self.output)? as u64;
written += std::io::copy(&mut &*entry.compressed_data, &mut self.output)? as u64;
}
}
None => {
let digest = self.output.hash.clone().digest();
self.output.write_all(&digest[..])?;
written += digest.len() as u64;
self.output.flush()?;
self.is_done = true;
}
};
Ok(written)
}
}
impl<I, W, E> Iterator for EntriesToBytesIter<I, W>
where
I: Iterator<Item = Result<Vec<output::Entry>, E>>,
W: std::io::Write,
E: std::error::Error + 'static,
{
type Item = Result<u64, Error<E>>;
fn next(&mut self) -> Option<Self::Item> {
if self.is_done {
return None;
}
Some(match self.next_inner() {
Err(err) => {
self.is_done = true;
Err(err)
}
Ok(written) => Ok(written),
})
}
}