#[cfg(test)]
mod tests;
use super::{ArtifactError, ArtifactIdentity, BoundedWriter, Sha256Digest, visit_reader};
use sha2::{Digest, Sha256};
use std::{
fmt,
io::{self, Read, Write},
};
#[derive(Debug)]
pub enum CopyError {
Input(ArtifactError),
Output(io::Error),
}
impl fmt::Display for CopyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Input(source) => write!(f, "copy input failed: {source}"),
Self::Output(source) => write!(f, "copy output failed: {source}"),
}
}
}
impl std::error::Error for CopyError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Input(source) => Some(source),
Self::Output(source) => Some(source),
}
}
}
impl From<ArtifactError> for CopyError {
fn from(source: ArtifactError) -> Self {
Self::Input(source)
}
}
impl From<CopyError> for io::Error {
fn from(error: CopyError) -> Self {
match error {
CopyError::Input(ArtifactError::Io(source)) | CopyError::Output(source) => source,
other @ CopyError::Input(_) => Self::other(other),
}
}
}
pub fn copy_reader(
mut reader: impl Read,
writer: &mut impl Write,
max_bytes: u64,
) -> Result<ArtifactIdentity, CopyError> {
let mut hasher = Sha256::new();
let mut writer = BoundedWriter::new(writer, max_bytes);
let bytes = visit_reader::<CopyError>(&mut reader, max_bytes, |chunk| {
writer.write_all(chunk).map_err(CopyError::Output)?;
hasher.update(chunk);
Ok(())
})?;
Ok(ArtifactIdentity {
bytes,
sha256: Sha256Digest::from_bytes(hasher.finalize().into()),
})
}