|
|
|
@ -1,4 +1,4 @@ |
|
|
|
|
use crate::error::unexpected::{self, Mappable}; |
|
|
|
|
use crate::error::unexpected::{self, Intoable, Mappable}; |
|
|
|
|
use crate::{origin, util}; |
|
|
|
|
use std::{collections, sync}; |
|
|
|
|
|
|
|
|
@ -7,6 +7,15 @@ struct DescrState { |
|
|
|
|
current_tree: gix_hash::ObjectId, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(thiserror::Error, Clone, Debug, PartialEq)] |
|
|
|
|
enum GetObjectError { |
|
|
|
|
#[error("unavailable due to server-side issue")] |
|
|
|
|
Unavailable, |
|
|
|
|
|
|
|
|
|
#[error(transparent)] |
|
|
|
|
Unexpected(#[from] unexpected::Error), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Default)] |
|
|
|
|
pub struct Proxy { |
|
|
|
|
client: reqwest::Client, |
|
|
|
@ -101,7 +110,8 @@ impl Proxy { |
|
|
|
|
&self, |
|
|
|
|
descr: &origin::Descr, |
|
|
|
|
oid: &gix_hash::ObjectId, |
|
|
|
|
) -> unexpected::Result<Option<util::BoxByteStream>> { |
|
|
|
|
expect_kind: gix_object::Kind, |
|
|
|
|
) -> Result<util::BoxByteStream, GetObjectError> { |
|
|
|
|
let hex = oid.to_string(); |
|
|
|
|
let (url, _) = Self::deconstruct_descr(descr); |
|
|
|
|
|
|
|
|
@ -109,26 +119,40 @@ impl Proxy { |
|
|
|
|
Self::construct_url(url, format!("objects/{}/{}", &hex[..2], &hex[2..]).as_str()) |
|
|
|
|
.or_unexpected_while("constructing refs url")?; |
|
|
|
|
|
|
|
|
|
Ok(self |
|
|
|
|
let mut loose_object = self |
|
|
|
|
.client |
|
|
|
|
.get(object_url) |
|
|
|
|
.send() |
|
|
|
|
.await |
|
|
|
|
.or_unexpected_while("performing request")? |
|
|
|
|
.or(Err(GetObjectError::Unavailable))? |
|
|
|
|
.error_for_status() |
|
|
|
|
.ok() |
|
|
|
|
.map(|res| { |
|
|
|
|
use async_compression::tokio::bufread::ZlibDecoder; |
|
|
|
|
use futures::stream::TryStreamExt; |
|
|
|
|
use std::io; |
|
|
|
|
|
|
|
|
|
let r = tokio_util::io::StreamReader::new( |
|
|
|
|
tokio::io::BufReader::new(ZlibDecoder::new(tokio_util::io::StreamReader::new( |
|
|
|
|
res.bytes_stream() |
|
|
|
|
.map_err(|e| io::Error::new(io::ErrorKind::Other, e)), |
|
|
|
|
); |
|
|
|
|
))) |
|
|
|
|
}) |
|
|
|
|
.or(Err(GetObjectError::Unavailable))?; |
|
|
|
|
|
|
|
|
|
use tokio::io::AsyncBufReadExt; |
|
|
|
|
let mut header = Vec::<u8>::new(); |
|
|
|
|
loose_object |
|
|
|
|
.read_until(0, &mut header) |
|
|
|
|
.await |
|
|
|
|
.or(Err(GetObjectError::Unavailable))?; |
|
|
|
|
|
|
|
|
|
let (kind, _, _) = |
|
|
|
|
gix_object::decode::loose_header(&header).or(Err(GetObjectError::Unavailable))?; |
|
|
|
|
|
|
|
|
|
if kind != expect_kind { |
|
|
|
|
return Err(GetObjectError::Unavailable); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
util::BoxByteStream::from_async_read(ZlibDecoder::new(r)) |
|
|
|
|
})) |
|
|
|
|
Ok(util::BoxByteStream::from_async_read(loose_object)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async fn get_commit_tree( |
|
|
|
@ -137,17 +161,18 @@ impl Proxy { |
|
|
|
|
commit_hash: &gix_hash::ObjectId, |
|
|
|
|
) -> Result<gix_hash::ObjectId, origin::SyncError> { |
|
|
|
|
let commit_object_bytes = self |
|
|
|
|
.get_object(descr, commit_hash) |
|
|
|
|
.await? |
|
|
|
|
.ok_or(origin::SyncError::Unavailable)? |
|
|
|
|
.get_object(descr, commit_hash, gix_object::Kind::Commit) |
|
|
|
|
.await |
|
|
|
|
.map_err(|e| match e { |
|
|
|
|
GetObjectError::Unavailable => origin::SyncError::Unavailable, |
|
|
|
|
GetObjectError::Unexpected(_) => e.into_unexpected().into(), |
|
|
|
|
})? |
|
|
|
|
.read_to_end() |
|
|
|
|
.await |
|
|
|
|
.or(Err(origin::SyncError::Unavailable))?; |
|
|
|
|
|
|
|
|
|
let commit_object = gix_object::ObjectRef::from_loose(commit_object_bytes.as_ref()) |
|
|
|
|
.or(Err(origin::SyncError::Unavailable))? |
|
|
|
|
.into_commit() |
|
|
|
|
.ok_or(origin::SyncError::Unavailable)?; |
|
|
|
|
let commit_object = gix_object::CommitRef::from_bytes(commit_object_bytes.as_ref()) |
|
|
|
|
.or(Err(origin::SyncError::Unavailable))?; |
|
|
|
|
|
|
|
|
|
Ok(commit_object.tree()) |
|
|
|
|
} |
|
|
|
@ -159,17 +184,18 @@ impl Proxy { |
|
|
|
|
entry_name: &str, |
|
|
|
|
) -> Result<gix_object::tree::Entry, origin::GetFileError> { |
|
|
|
|
let tree_object_bytes = self |
|
|
|
|
.get_object(descr, tree_hash) |
|
|
|
|
.await? |
|
|
|
|
.ok_or(origin::GetFileError::Unavailable)? |
|
|
|
|
.get_object(descr, tree_hash, gix_object::Kind::Tree) |
|
|
|
|
.await |
|
|
|
|
.map_err(|e| match e { |
|
|
|
|
GetObjectError::Unavailable => origin::GetFileError::Unavailable, |
|
|
|
|
GetObjectError::Unexpected(_) => e.into_unexpected().into(), |
|
|
|
|
})? |
|
|
|
|
.read_to_end() |
|
|
|
|
.await |
|
|
|
|
.or(Err(origin::GetFileError::Unavailable))?; |
|
|
|
|
|
|
|
|
|
let tree_object = gix_object::ObjectRef::from_loose(tree_object_bytes.as_ref()) |
|
|
|
|
.or(Err(origin::GetFileError::Unavailable))? |
|
|
|
|
.into_tree() |
|
|
|
|
.ok_or(origin::GetFileError::Unavailable)?; |
|
|
|
|
let tree_object = gix_object::TreeRef::from_bytes(tree_object_bytes.as_ref()) |
|
|
|
|
.or(Err(origin::GetFileError::Unavailable))?; |
|
|
|
|
|
|
|
|
|
for entry in tree_object.entries { |
|
|
|
|
if entry.filename == entry_name { |
|
|
|
@ -236,9 +262,7 @@ impl origin::Store for Proxy { |
|
|
|
|
let path = path |
|
|
|
|
.as_str() |
|
|
|
|
.parse::<std::path::PathBuf>() |
|
|
|
|
.or_unexpected_while("parsing path")? |
|
|
|
|
.canonicalize() |
|
|
|
|
.or_unexpected_while("canonicalizing path")?; |
|
|
|
|
.or_unexpected_while("parsing path")?; |
|
|
|
|
|
|
|
|
|
let path_parts = path.iter().collect::<Vec<&std::ffi::OsStr>>(); |
|
|
|
|
|
|
|
|
@ -297,12 +321,14 @@ impl origin::Store for Proxy { |
|
|
|
|
.into()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(self |
|
|
|
|
.get_object(&descr, &entry.oid) |
|
|
|
|
.await? |
|
|
|
|
.map_unexpected_while(|| format!("object for entry {:?} not found", entry))?) |
|
|
|
|
|
|
|
|
|
// TODO this is still not correct, as it will include the git object header
|
|
|
|
|
self.get_object(&descr, &entry.oid, gix_object::Kind::Blob) |
|
|
|
|
.await |
|
|
|
|
.map_err(|e| match e { |
|
|
|
|
GetObjectError::Unavailable => origin::GetFileError::Unavailable, |
|
|
|
|
GetObjectError::Unexpected(_) => e |
|
|
|
|
.into_unexpected_while(format!("getting object for entry {:?}", entry)) |
|
|
|
|
.into(), |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|