use std::{io::Write, thread::scope};
use pariter::IteratorExt;
use crate::{
backend::node::{Node, NodeType},
blob::{BlobId, BlobType, DataId},
error::{ErrorKind, RusticError, RusticResult},
index::ReadIndex,
repository::{IndexedFull, Repository},
};
pub(crate) fn dump<S: IndexedFull>(
repo: &Repository<S>,
node: &Node,
w: &mut impl Write,
) -> RusticResult<()> {
if node.node_type != NodeType::File {
return Err(RusticError::new(
ErrorKind::Unsupported,
"Dump is not supported for non-file node types `{node_type}`. You could try to use `cat` instead.",
)
.attach_context("node_type", node.node_type.to_string()));
}
let Some(content) = node.content.as_ref() else {
return Ok(());
};
if content.len() < 2 {
return dump_sequential(repo, content, w);
}
let index_entries: Vec<_> = content
.iter()
.map(|id| {
repo.index().get_data(id).ok_or_else(|| {
RusticError::new(ErrorKind::Internal, "Data Blob `{id}` not found in index")
.attach_context("id", id.to_string())
})
})
.collect::<RusticResult<_>>()?;
let be = repo.dbe();
scope(|s| -> RusticResult<()> {
index_entries
.iter()
.parallel_map_scoped(s, |ie| ie.read_data(be))
.try_for_each(|res| write_blob(w, &res?))
})
}
fn dump_sequential<S: IndexedFull>(
repo: &Repository<S>,
content: &[DataId],
w: &mut impl Write,
) -> RusticResult<()> {
for id in content {
let data = repo.get_blob_cached(&BlobId::from(**id), BlobType::Data)?;
write_blob(w, &data)?;
}
Ok(())
}
fn write_blob(w: &mut impl Write, data: &[u8]) -> RusticResult<()> {
w.write_all(data).map_err(|err| {
RusticError::with_source(
ErrorKind::InputOutput,
"Failed to write data to writer.",
err,
)
})
}