use bstr::{BString, ByteSlice};
use gix_transport::client::Capabilities;
use crate::{
fetch::{
refmap::{Mapping, Source, SpecIndex},
RefMap,
},
handshake::Ref,
};
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("The object format {format:?} as used by the remote is unsupported")]
UnknownObjectFormat { format: BString },
#[error(transparent)]
MappingValidation(#[from] gix_refspec::match_group::validate::Error),
#[error(transparent)]
ListRefs(#[from] crate::ls_refs::Error),
}
#[derive(Debug, Clone)]
pub struct Context {
pub fetch_refspecs: Vec<gix_refspec::RefSpec>,
pub extra_refspecs: Vec<gix_refspec::RefSpec>,
}
impl Context {
pub(crate) fn aggregate_refspecs(&self) -> Vec<gix_refspec::RefSpec> {
let mut all_refspecs = self.fetch_refspecs.clone();
all_refspecs.extend(self.extra_refspecs.iter().cloned());
all_refspecs
}
}
impl RefMap {
pub fn from_refs(remote_refs: Vec<Ref>, capabilities: &Capabilities, context: Context) -> Result<RefMap, Error> {
let all_refspecs = context.aggregate_refspecs();
let Context {
fetch_refspecs,
extra_refspecs,
} = context;
let num_explicit_specs = fetch_refspecs.len();
let group = gix_refspec::MatchGroup::from_fetch_specs(all_refspecs.iter().map(gix_refspec::RefSpec::to_ref));
let null = gix_hash::ObjectId::null(gix_hash::Kind::Sha1); let (res, fixes) = group
.match_lhs(remote_refs.iter().map(|r| {
let (full_ref_name, target, object) = r.unpack();
gix_refspec::match_group::Item {
full_ref_name,
target: target.unwrap_or(&null),
object,
}
}))
.validated()?;
let mappings = res.mappings;
let mappings = mappings
.into_iter()
.map(|m| Mapping {
remote: m.item_index.map_or_else(
|| {
Source::ObjectId(match m.lhs {
gix_refspec::match_group::SourceRef::ObjectId(id) => id,
_ => unreachable!("no item index implies having an object id"),
})
},
|idx| Source::Ref(remote_refs[idx].clone()),
),
local: m.rhs.map(std::borrow::Cow::into_owned),
spec_index: if m.spec_index < num_explicit_specs {
SpecIndex::ExplicitInRemote(m.spec_index)
} else {
SpecIndex::Implicit(m.spec_index - num_explicit_specs)
},
})
.collect();
let object_hash = if let Some(object_format) = capabilities.capability("object-format").and_then(|c| c.value())
{
let object_format = object_format.to_str().map_err(|_| Error::UnknownObjectFormat {
format: object_format.into(),
})?;
match object_format {
"sha1" => gix_hash::Kind::Sha1,
unknown => return Err(Error::UnknownObjectFormat { format: unknown.into() }),
}
} else {
gix_hash::Kind::Sha1
};
Ok(Self {
mappings,
refspecs: fetch_refspecs,
extra_refspecs,
fixes,
remote_refs,
object_hash,
})
}
}