gix_refspec/match_group/
mod.rs1use std::collections::BTreeSet;
2
3use crate::{MatchGroup, RefSpecRef, parse::Operation, types::Mode};
4
5pub(crate) mod types;
6pub use types::{Item, Mapping, Source, SourceRef, match_lhs, match_rhs};
7
8pub mod validate;
10
11impl<'a> MatchGroup<'a> {
13 pub fn from_fetch_specs(specs: impl IntoIterator<Item = RefSpecRef<'a>>) -> Self {
15 MatchGroup {
16 specs: specs.into_iter().filter(|s| s.op == Operation::Fetch).collect(),
17 }
18 }
19
20 pub fn from_push_specs(specs: impl IntoIterator<Item = RefSpecRef<'a>>) -> Self {
22 MatchGroup {
23 specs: specs.into_iter().filter(|s| s.op == Operation::Push).collect(),
24 }
25 }
26}
27
28impl<'spec> MatchGroup<'spec> {
30 pub fn match_lhs<'item>(
40 self,
41 mut items: impl Iterator<Item = Item<'item>> + Clone,
42 ) -> match_lhs::Outcome<'spec, 'item> {
43 let mut out = Vec::new();
44 let mut seen = BTreeSet::default();
45 let mut push_unique = |mapping| {
46 if seen.insert(calculate_hash(&mapping)) {
47 out.push(mapping);
48 }
49 };
50 let mut matchers: Vec<Option<Matcher<'_>>> = self
51 .specs
52 .iter()
53 .copied()
54 .map(Matcher::from)
55 .enumerate()
56 .map(|(idx, m)| match m.lhs {
57 Some(Needle::Object(id)) => {
58 push_unique(Mapping {
59 item_index: None,
60 lhs: SourceRef::ObjectId(id),
61 rhs: m.rhs.map(Needle::to_bstr),
62 spec_index: idx,
63 });
64 None
65 }
66 _ => Some(m),
67 })
68 .collect();
69
70 let mut has_negation = false;
71 for (spec_index, (spec, matcher)) in self.specs.iter().zip(matchers.iter_mut()).enumerate() {
72 if spec.mode == Mode::Negative {
73 has_negation = true;
74 continue;
75 }
76 for (item_index, item) in items.clone().enumerate() {
77 let Some(matcher) = matcher else { continue };
78 let (matched, rhs) = matcher.matches_lhs(item);
79 if matched {
80 push_unique(Mapping {
81 item_index: Some(item_index),
82 lhs: SourceRef::FullName(item.full_ref_name.into()),
83 rhs,
84 spec_index,
85 });
86 }
87 }
88 }
89
90 if let Some(hash_kind) = has_negation.then(|| items.next().map(|i| i.target.kind())).flatten() {
91 let null_id = hash_kind.null();
92 for matcher in matchers
93 .into_iter()
94 .zip(self.specs.iter())
95 .filter_map(|(m, spec)| m.and_then(|m| (spec.mode == Mode::Negative).then_some(m)))
96 {
97 out.retain(|m| match &m.lhs {
98 SourceRef::ObjectId(_) => true,
99 SourceRef::FullName(name) => {
100 !matcher
101 .matches_lhs(Item {
102 full_ref_name: name.as_ref(),
103 target: &null_id,
104 object: None,
105 })
106 .0
107 }
108 });
109 }
110 }
111 match_lhs::Outcome {
112 group: self,
113 mappings: out,
114 }
115 }
116
117 pub fn match_rhs<'item>(
130 self,
131 mut items: impl Iterator<Item = Item<'item>> + Clone,
132 ) -> match_rhs::Outcome<'spec, 'item> {
133 let mut out = Vec::<Mapping<'spec, 'item>>::new();
134 let mut seen = BTreeSet::default();
135 let mut push_unique = |mapping| {
136 if seen.insert(calculate_hash(&mapping)) {
137 out.push(mapping);
138 }
139 };
140 let mut matchers: Vec<Matcher<'_>> = self.specs.iter().copied().map(Matcher::from).collect();
141
142 let mut has_negation = false;
143 for (spec_index, (spec, matcher)) in self.specs.iter().zip(matchers.iter_mut()).enumerate() {
144 if spec.mode == Mode::Negative {
145 has_negation = true;
146 continue;
147 }
148 for (item_index, item) in items.clone().enumerate() {
149 let (matched, lhs) = matcher.matches_rhs(item);
150 if let Some(lhs) = lhs.filter(|_| matched) {
151 push_unique(Mapping {
152 item_index: Some(item_index),
153 lhs: SourceRef::FullName(lhs),
154 rhs: Some(item.full_ref_name.into()),
155 spec_index,
156 });
157 }
158 }
159 }
160
161 if let Some(hash_kind) = has_negation.then(|| items.next().map(|i| i.target.kind())).flatten() {
162 let null_id = hash_kind.null();
163 for matcher in matchers
164 .into_iter()
165 .zip(self.specs.iter())
166 .filter_map(|(m, spec)| (spec.mode == Mode::Negative).then_some(m))
167 {
168 out.retain(|m| match &m.lhs {
169 SourceRef::ObjectId(_) => true,
170 SourceRef::FullName(name) => {
171 !matcher
172 .matches_lhs(Item {
173 full_ref_name: name.as_ref(),
174 target: &null_id,
175 object: None,
176 })
177 .0
178 }
179 });
180 }
181 }
182 match_rhs::Outcome {
183 group: self,
184 mappings: out,
185 }
186 }
187}
188
189fn calculate_hash<T: std::hash::Hash>(t: &T) -> u64 {
190 use std::hash::Hasher;
191 let mut s = std::collections::hash_map::DefaultHasher::new();
192 t.hash(&mut s);
193 s.finish()
194}
195
196mod util;
197use util::{Matcher, Needle};