f5b4ea5
//! blob Use case: implementation of `BlobOps` for `GitRepository`.
use gix::hash::ObjectId;
use crate::GitRepository;
use crate::blob::impls::BlobOps;
use crate::blob::types::{BlobContent, BlobMeta, LfsPointer, RawDownload};
use crate::validate;
const LFS_POINTER_PREFIX: &[u8] = b"version https://git-lfs.github.com/spec/v1\n";
fn is_binary(data: &[u8]) -> bool {
let limit = data.len().min(8000);
data[..limit].contains(&0)
}
fn parse_lfs_pointer(data: &[u8]) -> Option<LfsPointer> {
if !data.starts_with(LFS_POINTER_PREFIX) {
return None;
}
let text = std::str::from_utf8(data).ok()?;
let mut oid: Option<String> = None;
let mut size: Option<i64> = None;
for line in text.lines() {
let line = line.trim_end();
if let Some(rest) = line.strip_prefix("oid sha256:") {
oid = Some(rest.to_string());
} else if let Some(rest) = line.strip_prefix("size ") {
size = rest.parse().ok();
}
}
Some(LfsPointer {
version: "https://git-lfs.github.com/spec/v1".to_string(),
oid: oid?,
size: size?,
})
}
impl BlobOps for GitRepository {
fn blob_content(&self, id: &ObjectId) -> Result<BlobContent, crate::Error> {
if let Some(hit) = self.cache_get_oid::<BlobContent>("blob", id) {
validate::enforce_max_size(&hit.data, validate::MAX_BLOB_SIZE, "blob")?;
return Ok(hit);
}
let repo = self.repo()?;
let blob = repo
.find_blob(*id)
.map_err(|e| crate::Error::Find(e.to_string()))?;
let data = blob.data.clone();
validate::enforce_max_size(&data, validate::MAX_BLOB_SIZE, "blob")?;
let size = data.len() as i64;
let bin = is_binary(&data);
let v = BlobContent {
id: *id,
data,
size,
is_binary: bin,
};
self.cache_put_oid("blob", id, &v);
Ok(v)
}
fn blob_metadata(&self, id: &ObjectId) -> Result<BlobMeta, crate::Error> {
if let Some(hit) = self.cache_get_oid::<BlobMeta>("blob_meta", id) {
return Ok(hit);
}
let repo = self.repo()?;
let obj = repo
.find_object(*id)
.map_err(|e| crate::Error::Find(e.to_string()))?;
let data = &obj.data;
let size = obj.data.len() as i64;
let lfs = parse_lfs_pointer(data).is_some();
let v = BlobMeta {
id: *id,
size,
is_binary: is_binary(data),
is_lfs_pointer: lfs,
lfs: if lfs { parse_lfs_pointer(data) } else { None },
object_type: obj.kind.into(),
};
self.cache_put_oid("blob_meta", id, &v);
Ok(v)
}
fn blob_is_lfs_pointer(&self, data: &[u8]) -> Result<Option<LfsPointer>, crate::Error> {
Ok(parse_lfs_pointer(data))
}
fn blob_raw_download(&self, id: &ObjectId) -> Result<RawDownload, crate::Error> {
if let Some(hit) = self.cache_get_oid::<RawDownload>("blob_raw", id) {
return Ok(hit);
}
let repo = self.repo()?;
let obj = repo
.find_object(*id)
.map_err(|e| crate::Error::Find(e.to_string()))?;
let data = &obj.data;
let v = RawDownload {
id: *id,
size: obj.data.len() as i64,
is_lfs_pointer: parse_lfs_pointer(data).is_some(),
};
self.cache_put_oid("blob_raw", id, &v);
Ok(v)
}
}