1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
use std::collections::BTreeSet;
use crate::{parse::Operation, types::Mode, MatchGroup, RefSpecRef};
pub(crate) mod types;
pub use types::{Item, Mapping, Outcome, Source, SourceRef};
///
pub mod validate;
/// Initialization
impl<'a> MatchGroup<'a> {
/// Take all the fetch ref specs from `specs` get a match group ready.
pub fn from_fetch_specs(specs: impl IntoIterator<Item = RefSpecRef<'a>>) -> Self {
MatchGroup {
specs: specs.into_iter().filter(|s| s.op == Operation::Fetch).collect(),
}
}
}
/// Matching
impl<'a> MatchGroup<'a> {
/// Match all `items` against all fetch specs present in this group, returning deduplicated mappings from source to destination.
/// Note that this method only makes sense if the specs are indeed fetch specs and may panic otherwise.
///
/// Note that negative matches are not part of the return value, so they are not observable but will be used to remove mappings.
pub fn match_remotes<'item>(self, mut items: impl Iterator<Item = Item<'item>> + Clone) -> Outcome<'a, 'item> {
let mut out = Vec::new();
let mut seen = BTreeSet::default();
let mut push_unique = |mapping| {
if seen.insert(calculate_hash(&mapping)) {
out.push(mapping);
}
};
let mut matchers: Vec<Option<Matcher<'_>>> = self
.specs
.iter()
.copied()
.map(Matcher::from)
.enumerate()
.map(|(idx, m)| match m.lhs {
Some(Needle::Object(id)) => {
push_unique(Mapping {
item_index: None,
lhs: SourceRef::ObjectId(id),
rhs: m.rhs.map(Needle::to_bstr),
spec_index: idx,
});
None
}
_ => Some(m),
})
.collect();
let mut has_negation = false;
for (spec_index, (spec, matcher)) in self.specs.iter().zip(matchers.iter_mut()).enumerate() {
for (item_index, item) in items.clone().enumerate() {
if spec.mode == Mode::Negative {
has_negation = true;
continue;
}
if let Some(matcher) = matcher {
let (matched, rhs) = matcher.matches_lhs(item);
if matched {
push_unique(Mapping {
item_index: Some(item_index),
lhs: SourceRef::FullName(item.full_ref_name),
rhs,
spec_index,
})
}
}
}
}
if let Some(id) = has_negation.then(|| items.next().map(|i| i.target)).flatten() {
let null_id = gix_hash::ObjectId::null(id.kind());
for matcher in matchers
.into_iter()
.zip(self.specs.iter())
.filter_map(|(m, spec)| m.and_then(|m| (spec.mode == Mode::Negative).then_some(m)))
{
out.retain(|m| match m.lhs {
SourceRef::ObjectId(_) => true,
SourceRef::FullName(name) => {
!matcher
.matches_lhs(Item {
full_ref_name: name,
target: &null_id,
object: None,
})
.0
}
});
}
}
Outcome {
group: self,
mappings: out,
}
}
}
fn calculate_hash<T: std::hash::Hash>(t: &T) -> u64 {
use std::hash::Hasher;
let mut s = std::collections::hash_map::DefaultHasher::new();
t.hash(&mut s);
s.finish()
}
mod util;
use util::{Matcher, Needle};
|