use std::sync::Arc;
use sley_object::{CommitRef, EncodedObject, ObjectType};
use sley_odb::{FileObjectDatabase, ObjectReader};
use crate::{
GitError, MissingObjectContext, MissingObjectKind, ObjectFormat, ObjectId, Repository, Result,
};
#[derive(Debug, Clone)]
pub struct LoadedObject {
object: Arc<EncodedObject>,
}
impl LoadedObject {
pub fn header(&self) -> (ObjectType, u64) {
(self.object.object_type, self.object.body.len() as u64)
}
pub fn commit_ref(&self, format: ObjectFormat) -> Result<CommitRef<'_>> {
if self.object.object_type != ObjectType::Commit {
return Err(GitError::InvalidObject(format!(
"object is a {}, not a commit",
self.object.object_type.as_str()
)));
}
CommitRef::parse(format, &self.object.body)
}
pub fn encoded(&self) -> &Arc<EncodedObject> {
&self.object
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct BlobFetchOptions {
remote: Option<String>,
}
impl BlobFetchOptions {
pub fn new() -> Self {
Self::default()
}
pub fn from_remote(remote: impl Into<String>) -> Self {
Self {
remote: Some(remote.into()),
}
}
pub fn remote(&self) -> Option<&str> {
self.remote.as_deref()
}
}
#[derive(Debug, Clone)]
pub struct BlobStore<'repo> {
repo: &'repo Repository,
}
impl<'repo> BlobStore<'repo> {
pub(crate) fn new(repo: &'repo Repository) -> Self {
Self { repo }
}
pub fn read(&self, oid: ObjectId) -> Result<Vec<u8>> {
self.read_local_blob(oid, MissingObjectContext::Read)
}
pub async fn read_or_fetch(&self, oid: ObjectId, options: BlobFetchOptions) -> Result<Vec<u8>> {
self.read_or_fetch_blocking(oid, options)
}
pub fn read_or_fetch_blocking(
&self,
oid: ObjectId,
options: BlobFetchOptions,
) -> Result<Vec<u8>> {
let context = if options.remote().is_some() {
MissingObjectContext::RemoteBoundary
} else {
MissingObjectContext::Read
};
self.read_local_blob(oid, context)
}
fn read_local_blob(&self, oid: ObjectId, context: MissingObjectContext) -> Result<Vec<u8>> {
let object = self
.repo
.read_object(&oid)
.map_err(|err| match err.not_found_kind() {
Some(crate::NotFoundKind::Object { .. }) => {
GitError::object_kind_not_found_in(oid, MissingObjectKind::Blob, context)
}
_ => err,
})?;
if object.object_type != ObjectType::Blob {
return Err(GitError::InvalidObject(format!(
"object {oid} is a {}, not a blob",
object.object_type.as_str()
)));
}
Ok(object.body.clone())
}
}
impl Repository {
pub fn objects(&self) -> Arc<FileObjectDatabase> {
Arc::clone(&self.objects)
}
pub fn objects_mut(&self) -> FileObjectDatabase {
self.objects.as_ref().clone()
}
pub fn blobs(&self) -> BlobStore<'_> {
BlobStore::new(self)
}
pub fn refresh_objects(&self) {
self.objects.refresh_read_cache();
}
pub fn read_object_header(&self, oid: &ObjectId) -> Result<Option<(ObjectType, u64)>> {
self.objects.read_object_header(oid)
}
pub fn load_object(&self, oid: &ObjectId) -> Result<LoadedObject> {
Ok(LoadedObject {
object: ObjectReader::read_object(self.objects.as_ref(), oid)?,
})
}
}