use std::sync::atomic::{AtomicBool, Ordering};
use crate::{
EntryMode, ObjectId, ObjectKind, ObjectReadError, Objects, ReadLimits, TreeChange, TreeValue,
};
#[derive(Debug)]
pub struct BlobContent {
pub old: Vec<u8>,
pub new: Vec<u8>,
}
impl BlobContent {
pub fn read(
objects: &Objects,
change: &TreeChange,
read: ReadLimits,
max_bytes: usize,
cancel: &AtomicBool,
) -> Result<Self, ContentReadError> {
check(cancel)?;
validate_mode(change.old)?;
validate_mode(change.new)?;
let old = read_side(objects, change.old, read, max_bytes)?;
check(cancel)?;
let new = read_side(objects, change.new, read, max_bytes - old.len())?;
check(cancel)?;
Ok(Self { old, new })
}
}
fn check(cancel: &AtomicBool) -> Result<(), ContentReadError> {
if cancel.load(Ordering::Relaxed) {
Err(ContentReadError::Cancelled)
} else {
Ok(())
}
}
fn validate_mode(value: Option<TreeValue>) -> Result<(), ContentReadError> {
if let Some(value) = value {
match value.mode {
EntryMode::Blob | EntryMode::Executable | EntryMode::Symlink => (),
mode => return Err(ContentReadError::UnsupportedMode(mode)),
}
}
Ok(())
}
fn read_side(
objects: &Objects,
value: Option<TreeValue>,
mut limits: ReadLimits,
remaining: usize,
) -> Result<Vec<u8>, ContentReadError> {
let Some(value) = value else {
return Ok(Vec::new());
};
limits.max_object_bytes = limits.max_object_bytes.min(remaining);
let id = value.id;
let object = objects
.read(id, limits)
.map_err(|source| ContentReadError::Read { id, source })?
.ok_or(ContentReadError::Missing(id))?;
if object.kind() != ObjectKind::Blob {
return Err(ContentReadError::NotBlob {
id,
actual: object.kind(),
});
}
Ok(object.into_data())
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ContentReadError {
#[error("content diff does not support entry mode {0:?}")]
UnsupportedMode(EntryMode),
#[error("missing blob {0}")]
Missing(ObjectId),
#[error("expected blob {id}, found {actual:?}")]
NotBlob {
id: ObjectId,
actual: ObjectKind,
},
#[error("reading blob {id}: {source}")]
Read {
id: ObjectId,
#[source]
source: ObjectReadError,
},
#[error("content read cancelled")]
Cancelled,
}
#[cfg(test)]
mod tests;