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>) -> 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> + 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>> = 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: &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};