use std::collections::BTreeSet;
use crate::{parse::Operation, types::Mode, MatchGroup, RefSpecRef};
pub(crate) mod types;
pub use types::{Item, Mapping, Outcome, Source, SourceRef};
#[allow(clippy::empty_docs)]
pub mod validate;
impl<'a> MatchGroup<'a> {
pub fn from_fetch_specs(specs: impl IntoIterator<Item = RefSpecRef<'a>>) -> Self {
MatchGroup {
specs: specs.into_iter().filter(|s| s.op == Operation::Fetch).collect(),
}
}
pub fn from_push_specs(specs: impl IntoIterator<Item = RefSpecRef<'a>>) -> Self {
MatchGroup {
specs: specs.into_iter().filter(|s| s.op == Operation::Push).collect(),
}
}
}
impl<'a> MatchGroup<'a> {
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() {
if spec.mode == Mode::Negative {
has_negation = true;
continue;
}
for (item_index, item) in items.clone().enumerate() {
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(hash_kind) = has_negation.then(|| items.next().map(|i| i.target.kind())).flatten() {
let null_id = hash_kind.null();
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};