gix_protocol/fetch/refmap/
init.rs1use bstr::{BString, ByteSlice};
2use gix_transport::client::Capabilities;
3
4use crate::{
5 fetch::{
6 RefMap,
7 refmap::{Mapping, Source, SpecIndex},
8 },
9 handshake::Ref,
10};
11
12#[derive(Debug, thiserror::Error)]
14#[allow(missing_docs)]
15pub enum Error {
16 #[error("The object format {format:?} as used by the remote is unsupported")]
17 UnknownObjectFormat { format: BString },
18 #[error(transparent)]
19 MappingValidation(#[from] gix_refspec::match_group::validate::Error),
20 #[error(transparent)]
21 ListRefs(#[from] crate::ls_refs::Error),
22}
23
24#[derive(Debug, Clone)]
26pub struct Context {
27 pub fetch_refspecs: Vec<gix_refspec::RefSpec>,
30 pub extra_refspecs: Vec<gix_refspec::RefSpec>,
34}
35
36impl Context {
37 pub(crate) fn aggregate_refspecs(&self) -> Vec<gix_refspec::RefSpec> {
38 let mut all_refspecs = self.fetch_refspecs.clone();
39 all_refspecs.extend(self.extra_refspecs.iter().cloned());
40 all_refspecs
41 }
42}
43
44impl RefMap {
45 pub fn from_refs(remote_refs: Vec<Ref>, capabilities: &Capabilities, context: Context) -> Result<RefMap, Error> {
48 let all_refspecs = context.aggregate_refspecs();
49 let Context {
50 fetch_refspecs,
51 extra_refspecs,
52 } = context;
53 let num_explicit_specs = fetch_refspecs.len();
54 let group = gix_refspec::MatchGroup::from_fetch_specs(all_refspecs.iter().map(gix_refspec::RefSpec::to_ref));
55 let object_hash = extract_object_hash(capabilities)?;
56 let null = object_hash.null();
57 let (res, fixes) = group
58 .match_lhs(remote_refs.iter().map(|r| {
59 let (full_ref_name, target, object) = r.unpack();
60 gix_refspec::match_group::Item {
61 full_ref_name,
62 target: target.unwrap_or(&null),
63 object,
64 }
65 }))
66 .validated()?;
67
68 let mappings = res.mappings;
69 let mappings = mappings
70 .into_iter()
71 .map(|m| Mapping {
72 remote: m.item_index.map_or_else(
73 || {
74 Source::ObjectId(match m.lhs {
75 gix_refspec::match_group::SourceRef::ObjectId(id) => id,
76 _ => unreachable!("no item index implies having an object id"),
77 })
78 },
79 |idx| Source::Ref(remote_refs[idx].clone()),
80 ),
81 local: m.rhs.map(std::borrow::Cow::into_owned),
82 spec_index: if m.spec_index < num_explicit_specs {
83 SpecIndex::ExplicitInRemote(m.spec_index)
84 } else {
85 SpecIndex::Implicit(m.spec_index - num_explicit_specs)
86 },
87 })
88 .collect();
89
90 Ok(Self {
91 mappings,
92 refspecs: fetch_refspecs,
93 extra_refspecs,
94 fixes,
95 remote_refs,
96 object_hash,
97 })
98 }
99}
100
101fn extract_object_hash(capabilities: &Capabilities) -> Result<gix_hash::Kind, Error> {
107 let object_format = match capabilities.capability("object-format").and_then(|c| c.value()) {
108 Some(object_format) => object_format.to_str().map_err(|_| Error::UnknownObjectFormat {
109 format: object_format.into(),
110 })?,
111 None => "sha1",
112 };
113 object_format
114 .parse::<gix_hash::Kind>()
115 .map_err(|_| Error::UnknownObjectFormat {
116 format: object_format.into(),
117 })
118}