1use bstr::{BString, ByteSlice};
2use gix_glob::Pattern;
3use kstring::{KString, KStringRef};
4
5use crate::{
6 search::{
7 refmap::RefMapKey, Assignments, AttributeId, Attributes, MatchKind, Metadata, MetadataCollection, Outcome,
8 TrackedAssignment, Value,
9 },
10 AssignmentRef, NameRef, StateRef,
11};
12
13impl Outcome {
15 pub fn initialize(&mut self, collection: &MetadataCollection) {
20 if self.matches_by_id.len() != collection.name_to_meta.len() {
21 let global_num_attrs = collection.name_to_meta.len();
22
23 self.matches_by_id.resize(global_num_attrs, Default::default());
24
25 for (order, macro_attributes) in collection.iter().filter_map(|(_, meta)| {
27 (!meta.macro_attributes.is_empty()).then_some((meta.id.0, &meta.macro_attributes))
28 }) {
29 self.matches_by_id[order].macro_attributes.clone_from(macro_attributes);
30 }
31
32 for (name, id) in self.selected.iter_mut().filter(|(_, id)| id.is_none()) {
33 *id = collection.name_to_meta.get(name.as_str()).map(|meta| meta.id);
34 }
35 }
36 self.reset();
37 }
38
39 pub fn initialize_with_selection<'a>(
45 &mut self,
46 collection: &MetadataCollection,
47 attribute_names: impl IntoIterator<Item = impl Into<KStringRef<'a>>>,
48 ) {
49 self.initialize_with_selection_inner(collection, &mut attribute_names.into_iter().map(Into::into));
50 }
51
52 fn initialize_with_selection_inner(
53 &mut self,
54 collection: &MetadataCollection,
55 attribute_names: &mut dyn Iterator<Item = KStringRef<'_>>,
56 ) {
57 self.selected.clear();
58 self.selected.extend(attribute_names.map(|name| {
59 (
60 name.to_owned(),
61 collection.name_to_meta.get(name.as_str()).map(|meta| meta.id),
62 )
63 }));
64
65 self.initialize(collection);
66 self.reset_remaining();
67 }
68
69 pub fn reset(&mut self) {
71 self.matches_by_id.iter_mut().for_each(|item| item.r#match = None);
72 self.attrs_stack.clear();
73 self.reset_remaining();
74 }
75
76 fn reset_remaining(&mut self) {
77 self.remaining = Some(if self.selected.is_empty() {
78 self.matches_by_id.len()
79 } else {
80 self.selected.iter().filter(|(_name, id)| id.is_some()).count()
81 });
82 }
83
84 pub fn copy_into(&self, collection: &MetadataCollection, dest: &mut Self) {
90 dest.initialize(collection);
91 dest.matches_by_id.clone_from(&self.matches_by_id);
92 if dest.patterns.len() != self.patterns.len() {
93 dest.patterns = self.patterns.clone();
94 }
95 if dest.assignments.len() != self.assignments.len() {
96 dest.assignments = self.assignments.clone();
97 }
98 if dest.source_paths.len() != self.source_paths.len() {
99 dest.source_paths = self.source_paths.clone();
100 }
101 dest.remaining = self.remaining;
102 }
103}
104
105impl Outcome {
107 pub fn iter(&self) -> impl Iterator<Item = crate::search::Match<'_>> {
121 self.matches_by_id
122 .iter()
123 .filter_map(|item| item.r#match.as_ref().map(|m| m.to_outer(self)))
124 }
125
126 pub fn iter_selected(&self) -> impl Iterator<Item = crate::search::Match<'_>> {
130 static DUMMY: Pattern = Pattern {
131 text: BString::new(Vec::new()),
132 mode: gix_glob::pattern::Mode::empty(),
133 first_wildcard_pos: None,
134 };
135 self.selected.iter().map(|(name, id)| {
136 id.and_then(|id| self.matches_by_id[id.0].r#match.as_ref().map(|m| m.to_outer(self)))
137 .unwrap_or_else(|| crate::search::Match {
138 pattern: &DUMMY,
139 assignment: AssignmentRef {
140 name: NameRef::try_from(name.as_bytes().as_bstr())
141 .unwrap_or_else(|_| NameRef("invalid".into())),
142 state: StateRef::Unspecified,
143 },
144 kind: MatchKind::Attribute { macro_id: None },
145 location: crate::search::MatchLocation {
146 source: None,
147 sequence_number: 0,
148 },
149 })
150 })
151 }
152
153 pub fn match_by_id(&self, id: AttributeId) -> Option<crate::search::Match<'_>> {
155 self.matches_by_id
156 .get(id.0)
157 .and_then(|m| m.r#match.as_ref().map(|m| m.to_outer(self)))
158 }
159
160 pub fn is_done(&self) -> bool {
162 self.remaining() == 0
163 }
164}
165
166impl Outcome {
168 pub(crate) fn fill_attributes<'a>(
173 &mut self,
174 attrs: impl Iterator<Item = &'a TrackedAssignment>,
175 pattern: &gix_glob::Pattern,
176 source: Option<&std::path::PathBuf>,
177 sequence_number: usize,
178 ) -> bool {
179 self.attrs_stack.extend(
180 attrs
181 .filter(|attr| self.matches_by_id[attr.id.0].r#match.is_none())
182 .map(|attr| (attr.id, attr.inner.clone(), None)),
183 );
184 while let Some((id, assignment, parent_order)) = self.attrs_stack.pop() {
185 let slot = &mut self.matches_by_id[id.0];
186 if slot.r#match.is_some() {
187 continue;
188 }
189 let is_macro = !slot.macro_attributes.is_empty();
191
192 slot.r#match = Some(Match {
193 pattern: self.patterns.insert(pattern),
194 assignment: self.assignments.insert_owned(assignment),
195 kind: if is_macro {
196 MatchKind::Macro {
197 parent_macro_id: parent_order,
198 }
199 } else {
200 MatchKind::Attribute { macro_id: parent_order }
201 },
202 location: MatchLocation {
203 source: source.map(|path| self.source_paths.insert(path)),
204 sequence_number,
205 },
206 });
207 if self.reduce_and_check_if_done(id) {
208 return true;
209 }
210
211 if is_macro {
212 let slot = &self.matches_by_id[id.0];
214 self.attrs_stack.extend(
215 slot.macro_attributes
216 .iter()
217 .filter(|attr| self.matches_by_id[attr.id.0].r#match.is_none())
218 .map(|attr| (attr.id, attr.inner.clone(), Some(id))),
219 );
220 }
221 }
222 false
223 }
224}
225
226impl Outcome {
227 pub(crate) fn has_unspecified_attributes(&self, mut attrs: impl Iterator<Item = AttributeId>) -> bool {
229 attrs.any(|order| self.matches_by_id[order.0].r#match.is_none())
230 }
231 pub(crate) fn remaining(&self) -> usize {
235 self.remaining
236 .expect("BUG: instance must be initialized for each search set")
237 }
238
239 fn reduce_and_check_if_done(&mut self, attr: AttributeId) -> bool {
240 if self.selected.is_empty() || self.selected.iter().any(|(_name, id)| *id == Some(attr)) {
241 *self.remaining.as_mut().expect("initialized") -= 1;
242 }
243 self.is_done()
244 }
245}
246
247impl std::fmt::Debug for Outcome {
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 struct AsDisplay<'a>(&'a dyn std::fmt::Display);
250 impl std::fmt::Debug for AsDisplay<'_> {
251 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252 self.0.fmt(f)
253 }
254 }
255
256 let mut dbg = f.debug_tuple("Outcome");
257 if self.selected.is_empty() {
258 for match_ in self.iter() {
259 dbg.field(&AsDisplay(&match_.assignment));
260 }
261 } else {
262 for match_ in self.iter_selected() {
263 dbg.field(&AsDisplay(&match_.assignment));
264 }
265 }
266 dbg.finish()
267 }
268}
269
270impl MetadataCollection {
272 pub fn update_from_list(&mut self, list: &mut gix_glob::search::pattern::List<Attributes>) {
277 for pattern in &mut list.patterns {
278 match &mut pattern.value {
279 Value::MacroAssignments { id: order, assignments } => {
280 *order = self.id_for_macro(
281 pattern
282 .pattern
283 .text
284 .to_str()
285 .expect("valid macro names are always UTF8 and this was verified"),
286 assignments,
287 );
288 }
289 Value::Assignments(assignments) => {
290 self.assign_order_to_attributes(assignments);
291 }
292 }
293 }
294 }
295}
296
297impl MetadataCollection {
299 pub fn iter(&self) -> impl Iterator<Item = (&str, &Metadata)> {
301 self.name_to_meta.iter().map(|(k, v)| (k.as_str(), v))
302 }
303}
304
305impl MetadataCollection {
306 pub(crate) fn id_for_macro(&mut self, name: &str, attrs: &mut Assignments) -> AttributeId {
307 let order = match self.name_to_meta.get_mut(name) {
308 Some(meta) => meta.id,
309 None => {
310 let order = AttributeId(self.name_to_meta.len());
311 self.name_to_meta.insert(
312 KString::from_ref(name),
313 Metadata {
314 id: order,
315 macro_attributes: Default::default(),
316 },
317 );
318 order
319 }
320 };
321
322 self.assign_order_to_attributes(attrs);
323 self.name_to_meta
324 .get_mut(name)
325 .expect("just added")
326 .macro_attributes
327 .clone_from(attrs);
328
329 order
330 }
331 pub(crate) fn id_for_attribute(&mut self, name: &str) -> AttributeId {
332 match self.name_to_meta.get(name) {
333 Some(meta) => meta.id,
334 None => {
335 let order = AttributeId(self.name_to_meta.len());
336 self.name_to_meta.insert(KString::from_ref(name), order.into());
337 order
338 }
339 }
340 }
341 pub(crate) fn assign_order_to_attributes(&mut self, attributes: &mut [TrackedAssignment]) {
342 for TrackedAssignment {
343 id: order,
344 inner: crate::Assignment { name, .. },
345 } in attributes
346 {
347 *order = self.id_for_attribute(&name.0);
348 }
349 }
350}
351
352impl From<AttributeId> for Metadata {
353 fn from(order: AttributeId) -> Self {
354 Metadata {
355 id: order,
356 macro_attributes: Default::default(),
357 }
358 }
359}
360
361impl MatchKind {
362 pub fn source_id(&self) -> Option<AttributeId> {
364 match self {
365 MatchKind::Attribute { macro_id: id } | MatchKind::Macro { parent_macro_id: id } => *id,
366 }
367 }
368}
369
370#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
372pub struct Match {
373 pub pattern: RefMapKey,
375 pub assignment: RefMapKey,
377 pub kind: MatchKind,
379 pub location: MatchLocation,
381}
382
383impl Match {
384 fn to_outer<'a>(&self, out: &'a Outcome) -> crate::search::Match<'a> {
385 crate::search::Match {
386 pattern: out.patterns.resolve(self.pattern).expect("pattern still present"),
387 assignment: out
388 .assignments
389 .resolve(self.assignment)
390 .expect("assignment present")
391 .as_ref(),
392 kind: self.kind,
393 location: self.location.to_outer(out),
394 }
395 }
396}
397
398#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
400pub struct MatchLocation {
401 pub source: Option<RefMapKey>,
403 pub sequence_number: usize,
405}
406
407impl MatchLocation {
408 fn to_outer<'a>(&self, out: &'a Outcome) -> crate::search::MatchLocation<'a> {
409 crate::search::MatchLocation {
410 source: self
411 .source
412 .and_then(|source| out.source_paths.resolve(source).map(AsRef::as_ref)),
413 sequence_number: self.sequence_number,
414 }
415 }
416}