use crate::commit::topo::iter::gen_and_commit_time;
use crate::commit::topo::{Error, Sorting, WalkFlags};
use crate::commit::{find, Info, Parents, Topo};
use gix_hash::{oid, ObjectId};
use gix_revwalk::graph::IdMap;
use gix_revwalk::PriorityQueue;
pub struct Builder<Find, Predicate> {
commit_graph: Option<gix_commitgraph::Graph>,
find: Find,
predicate: Predicate,
sorting: Sorting,
parents: Parents,
tips: Vec<ObjectId>,
ends: Vec<ObjectId>,
}
impl<Find> Builder<Find, fn(&oid) -> bool>
where
Find: gix_object::Find,
{
pub fn from_iters(
find: Find,
tips: impl IntoIterator<Item = impl Into<ObjectId>>,
ends: Option<impl IntoIterator<Item = impl Into<ObjectId>>>,
) -> Self {
let tips = tips.into_iter().map(Into::into).collect::<Vec<_>>();
let ends = ends
.map(|e| e.into_iter().map(Into::into).collect::<Vec<_>>())
.unwrap_or_default();
Self {
commit_graph: Default::default(),
find,
sorting: Default::default(),
parents: Default::default(),
tips,
ends,
predicate: |_| true,
}
}
pub fn with_predicate<Predicate>(self, predicate: Predicate) -> Builder<Find, Predicate>
where
Predicate: FnMut(&oid) -> bool,
{
Builder {
commit_graph: self.commit_graph,
find: self.find,
sorting: self.sorting,
parents: self.parents,
tips: self.tips,
ends: self.ends,
predicate,
}
}
}
impl<Find, Predicate> Builder<Find, Predicate>
where
Find: gix_object::Find,
Predicate: FnMut(&oid) -> bool,
{
pub fn sorting(mut self, sorting: Sorting) -> Self {
self.sorting = sorting;
self
}
pub fn parents(mut self, parents: Parents) -> Self {
self.parents = parents;
self
}
pub fn with_commit_graph(mut self, commit_graph: Option<gix_commitgraph::Graph>) -> Self {
self.commit_graph = commit_graph;
self
}
pub fn build(self) -> Result<Topo<Find, Predicate>, Error> {
let mut w = Topo {
commit_graph: self.commit_graph,
find: self.find,
predicate: self.predicate,
indegrees: IdMap::default(),
states: IdMap::default(),
explore_queue: PriorityQueue::new(),
indegree_queue: PriorityQueue::new(),
topo_queue: super::iter::Queue::new(self.sorting),
parents: self.parents,
min_gen: gix_commitgraph::GENERATION_NUMBER_INFINITY,
buf: vec![],
};
let tip_flags = WalkFlags::Seen | WalkFlags::Explored | WalkFlags::InDegree;
let end_flags = tip_flags | WalkFlags::Uninteresting | WalkFlags::Bottom;
for (id, flags) in self
.tips
.iter()
.map(|id| (id, tip_flags))
.chain(self.ends.iter().map(|id| (id, end_flags)))
{
*w.indegrees.entry(*id).or_default() = 1;
let commit = find(w.commit_graph.as_ref(), &w.find, id, &mut w.buf)?;
let (gen, time) = gen_and_commit_time(commit)?;
if gen < w.min_gen {
w.min_gen = gen;
}
w.states.insert(*id, flags);
w.explore_queue.insert((gen, time), *id);
w.indegree_queue.insert((gen, time), *id);
}
for id in &self.ends {
let parents = w.collect_all_parents(id)?;
for (id, _) in parents {
w.states
.entry(id)
.and_modify(|s| *s |= WalkFlags::Uninteresting)
.or_insert(WalkFlags::Uninteresting | WalkFlags::Seen);
}
}
w.compute_indegrees_to_depth(w.min_gen)?;
for id in self.tips.iter() {
let i = w.indegrees.get(id).ok_or(Error::MissingIndegreeUnexpected)?;
if *i != 1 {
continue;
}
let commit = find(w.commit_graph.as_ref(), &w.find, id, &mut w.buf)?;
let (_, time) = gen_and_commit_time(commit)?;
let parent_ids = w.collect_all_parents(id)?.into_iter().map(|e| e.0).collect();
w.topo_queue.push(
time,
Info {
id: *id,
parent_ids,
commit_time: Some(time),
},
);
}
w.topo_queue.initial_sort();
Ok(w)
}
}