summaryrefslogtreecommitdiffstats
path: root/vendor/gix-refspec/src/match_group/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-refspec/src/match_group/mod.rs')
-rw-r--r--vendor/gix-refspec/src/match_group/mod.rs112
1 files changed, 112 insertions, 0 deletions
diff --git a/vendor/gix-refspec/src/match_group/mod.rs b/vendor/gix-refspec/src/match_group/mod.rs
new file mode 100644
index 000000000..c53b5b531
--- /dev/null
+++ b/vendor/gix-refspec/src/match_group/mod.rs
@@ -0,0 +1,112 @@
+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<Item = RefSpecRef<'a>>) -> 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<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(|n| n.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: 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};