gix_refspec/match_group/
mod.rs1use std::collections::BTreeSet;
2
3use crate::{parse::Operation, types::Mode, MatchGroup, RefSpecRef};
4
5pub(crate) mod types;
6pub use types::{match_lhs, match_rhs, Item, Mapping, Source, SourceRef};
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>(
125 self,
126 mut items: impl Iterator<Item = Item<'item>> + Clone,
127 ) -> match_rhs::Outcome<'spec, 'item> {
128 let mut out = Vec::<Mapping<'spec, 'item>>::new();
129 let mut seen = BTreeSet::default();
130 let mut push_unique = |mapping| {
131 if seen.insert(calculate_hash(&mapping)) {
132 out.push(mapping);
133 }
134 };
135 let mut matchers: Vec<Matcher<'_>> = self.specs.iter().copied().map(Matcher::from).collect();
136
137 let mut has_negation = false;
138 for (spec_index, (spec, matcher)) in self.specs.iter().zip(matchers.iter_mut()).enumerate() {
139 if spec.mode == Mode::Negative {
140 has_negation = true;
141 continue;
142 }
143 for (item_index, item) in items.clone().enumerate() {
144 let (matched, lhs) = matcher.matches_rhs(item);
145 if let Some(lhs) = lhs.filter(|_| matched) {
146 push_unique(Mapping {
147 item_index: Some(item_index),
148 lhs: SourceRef::FullName(lhs),
149 rhs: Some(item.full_ref_name.into()),
150 spec_index,
151 });
152 }
153 }
154 }
155
156 if let Some(hash_kind) = has_negation.then(|| items.next().map(|i| i.target.kind())).flatten() {
157 let null_id = hash_kind.null();
158 for matcher in matchers
159 .into_iter()
160 .zip(self.specs.iter())
161 .filter_map(|(m, spec)| (spec.mode == Mode::Negative).then_some(m))
162 {
163 out.retain(|m| match &m.lhs {
164 SourceRef::ObjectId(_) => true,
165 SourceRef::FullName(name) => {
166 !matcher
167 .matches_rhs(Item {
168 full_ref_name: name.as_ref(),
169 target: &null_id,
170 object: None,
171 })
172 .0
173 }
174 });
175 }
176 }
177 match_rhs::Outcome {
178 group: self,
179 mappings: out,
180 }
181 }
182}
183
184fn calculate_hash<T: std::hash::Hash>(t: &T) -> u64 {
185 use std::hash::Hasher;
186 let mut s = std::collections::hash_map::DefaultHasher::new();
187 t.hash(&mut s);
188 s.finish()
189}
190
191mod util;
192use util::{Matcher, Needle};