use crate::graph::{
DependencyDirection, DependencyEdge, DependencyLink, PackageGraph, PackageMetadata,
};
use crate::petgraph_support::reversed::ReversedDirected;
use crate::petgraph_support::walk::EdgeDfs;
use crate::{Error, PackageId};
use derivative::Derivative;
use fixedbitset::FixedBitSet;
use petgraph::prelude::*;
use petgraph::visit::{IntoNeighbors, NodeFiltered, Topo, VisitMap, Visitable};
#[derive(Clone, Debug)]
pub struct PackageSelect<'g> {
pub(super) package_graph: &'g PackageGraph,
pub(super) params: PackageSelectParams,
}
impl PackageGraph {
pub fn select_all<'g>(&'g self) -> PackageSelect<'g> {
PackageSelect {
package_graph: self,
params: PackageSelectParams::All,
}
}
pub fn select_transitive_deps_directed<'g, 'a>(
&'g self,
package_ids: impl IntoIterator<Item = &'a PackageId>,
dep_direction: DependencyDirection,
) -> Result<PackageSelect<'g>, Error> {
match dep_direction {
DependencyDirection::Forward => self.select_transitive_deps(package_ids),
DependencyDirection::Reverse => self.select_transitive_reverse_deps(package_ids),
}
}
pub fn select_transitive_deps<'g, 'a>(
&'g self,
package_ids: impl IntoIterator<Item = &'a PackageId>,
) -> Result<PackageSelect<'g>, Error> {
Ok(PackageSelect {
package_graph: self,
params: PackageSelectParams::TransitiveDeps(self.node_idxs(package_ids)?),
})
}
pub fn select_transitive_reverse_deps<'g, 'a>(
&'g self,
package_ids: impl IntoIterator<Item = &'a PackageId>,
) -> Result<PackageSelect<'g>, Error> {
Ok(PackageSelect {
package_graph: self,
params: PackageSelectParams::TransitiveReverseDeps(self.node_idxs(package_ids)?),
})
}
}
impl<'g> PackageSelect<'g> {
pub fn into_root_ids(
self,
direction: DependencyDirection,
) -> impl Iterator<Item = &'g PackageId> + ExactSizeIterator + 'g {
let dep_graph = self.package_graph.dep_graph();
let (_, roots) = select_postfilter(dep_graph, self.params, direction);
roots.into_iter().map(move |node_idx| &dep_graph[node_idx])
}
pub fn into_iter_ids(self, direction_opt: Option<DependencyDirection>) -> IntoIterIds<'g> {
let direction = direction_opt.unwrap_or_else(|| self.params.default_direction());
let dep_graph = self.package_graph.dep_graph();
let (reachable, count) = select_prefilter(dep_graph, self.params);
let filtered_graph = NodeFiltered(dep_graph, reachable);
let topo = match direction {
DependencyDirection::Forward => Topo::new(&filtered_graph),
DependencyDirection::Reverse => Topo::new(ReversedDirected(&filtered_graph)),
};
IntoIterIds {
graph: filtered_graph,
topo,
direction,
remaining: count,
}
}
pub fn into_iter_metadatas(
self,
direction_opt: Option<DependencyDirection>,
) -> IntoIterMetadatas<'g> {
let package_graph = self.package_graph;
let inner = self.into_iter_ids(direction_opt);
IntoIterMetadatas {
package_graph,
inner,
}
}
pub fn into_iter_links(self, direction_opt: Option<DependencyDirection>) -> IntoIterLinks<'g> {
use DependencyDirection::*;
let direction = direction_opt.unwrap_or_else(|| self.params.default_direction());
let dep_graph = self.package_graph.dep_graph();
let (reachable, roots) = select_postfilter(dep_graph, self.params, direction);
let (reachable, edge_dfs) = match (reachable, direction) {
(Some(reachable), Forward) => {
let filtered_graph = NodeFiltered(dep_graph, reachable);
let edge_dfs = EdgeDfs::new(&filtered_graph, roots);
(Some(filtered_graph.1), edge_dfs)
}
(Some(reachable), Reverse) => {
let filtered_reversed_graph = NodeFiltered(ReversedDirected(dep_graph), reachable);
let edge_dfs = EdgeDfs::new(&filtered_reversed_graph, roots);
(Some(filtered_reversed_graph.1), edge_dfs)
}
(None, Forward) => (None, EdgeDfs::new(dep_graph, roots)),
(None, Reverse) => (None, EdgeDfs::new(ReversedDirected(dep_graph), roots)),
};
IntoIterLinks {
package_graph: self.package_graph,
reachable,
edge_dfs,
direction,
}
}
}
pub(super) fn select_prefilter(
graph: &Graph<PackageId, DependencyEdge>,
params: PackageSelectParams,
) -> (FixedBitSet, usize) {
use PackageSelectParams::*;
match params {
All => all_visit_map(graph),
TransitiveDeps(roots) => reachable_map(graph, roots),
TransitiveReverseDeps(roots) => reachable_map(ReversedDirected(graph), roots),
}
}
fn select_postfilter(
graph: &Graph<PackageId, DependencyEdge>,
params: PackageSelectParams,
direction: DependencyDirection,
) -> (Option<FixedBitSet>, Vec<NodeIndex<u32>>) {
use DependencyDirection::*;
use PackageSelectParams::*;
match (params, direction) {
(All, Forward) => {
let roots: Vec<_> = PackageGraph::roots(graph);
(None, roots)
}
(All, Reverse) => {
let reversed_graph = ReversedDirected(graph);
let roots: Vec<_> = PackageGraph::roots(reversed_graph);
(None, roots)
}
(TransitiveDeps(roots), Forward) => {
(None, roots)
}
(TransitiveDeps(roots), Reverse) => {
let (reachable, _) = reachable_map(graph, roots);
let filtered_reversed_graph = NodeFiltered(ReversedDirected(graph), reachable);
let roots: Vec<_> = PackageGraph::roots(&filtered_reversed_graph);
(Some(filtered_reversed_graph.1), roots)
}
(TransitiveReverseDeps(roots), Forward) => {
let reversed_graph = ReversedDirected(graph);
let (reachable, _) = reachable_map(reversed_graph, roots);
let filtered_graph = NodeFiltered(graph, reachable);
let roots: Vec<_> = PackageGraph::roots(&filtered_graph);
(Some(filtered_graph.1), roots)
}
(TransitiveReverseDeps(roots), Reverse) => {
(None, roots)
}
}
}
#[derive(Clone, Derivative)]
#[derivative(Debug)]
pub struct IntoIterIds<'g> {
#[derivative(Debug = "ignore")]
graph: NodeFiltered<&'g Graph<PackageId, DependencyEdge>, FixedBitSet>,
#[derivative(Debug = "ignore")]
topo: Topo<NodeIndex<u32>, FixedBitSet>,
direction: DependencyDirection,
remaining: usize,
}
impl<'g> IntoIterIds<'g> {
pub fn direction(&self) -> DependencyDirection {
self.direction
}
}
impl<'g> Iterator for IntoIterIds<'g> {
type Item = &'g PackageId;
fn next(&mut self) -> Option<Self::Item> {
let next_idx = match self.direction {
DependencyDirection::Forward => self.topo.next(&self.graph),
DependencyDirection::Reverse => self.topo.next(ReversedDirected(&self.graph)),
};
next_idx.map(|node_idx| {
self.remaining -= 1;
&self.graph.0[node_idx]
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.remaining, Some(self.remaining))
}
}
impl<'g> ExactSizeIterator for IntoIterIds<'g> {
fn len(&self) -> usize {
self.remaining
}
}
#[derive(Clone, Debug)]
pub struct IntoIterMetadatas<'g> {
package_graph: &'g PackageGraph,
inner: IntoIterIds<'g>,
}
impl<'g> IntoIterMetadatas<'g> {
pub fn direction(&self) -> DependencyDirection {
self.inner.direction
}
}
impl<'g> Iterator for IntoIterMetadatas<'g> {
type Item = &'g PackageMetadata;
fn next(&mut self) -> Option<Self::Item> {
let next_id = self.inner.next()?;
Some(
self.package_graph.metadata(next_id).unwrap_or_else(|| {
panic!("known package ID '{}' not found in metadata map", next_id)
}),
)
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'g> ExactSizeIterator for IntoIterMetadatas<'g> {
fn len(&self) -> usize {
self.inner.len()
}
}
#[derive(Clone, Derivative)]
#[derivative(Debug)]
pub struct IntoIterLinks<'g> {
#[derivative(Debug = "ignore")]
package_graph: &'g PackageGraph,
reachable: Option<FixedBitSet>,
edge_dfs: EdgeDfs<EdgeIndex<u32>, NodeIndex<u32>, FixedBitSet>,
direction: DependencyDirection,
}
impl<'g> IntoIterLinks<'g> {
pub fn direction(&self) -> DependencyDirection {
self.direction
}
fn next_triple(&mut self) -> Option<(NodeIndex<u32>, NodeIndex<u32>, EdgeIndex<u32>)> {
use DependencyDirection::*;
match (&self.reachable, self.direction) {
(Some(reachable), Forward) => self.edge_dfs.next(&NodeFiltered::from_fn(
self.package_graph.dep_graph(),
|node_idx| reachable.is_visited(&node_idx),
)),
(Some(reachable), Reverse) => {
self.edge_dfs
.next(&NodeFiltered::from_fn(
ReversedDirected(self.package_graph.dep_graph()),
|node_idx| reachable.is_visited(&node_idx),
))
.map(|(source_idx, target_idx, edge_idx)| {
(target_idx, source_idx, edge_idx)
})
}
(None, Forward) => self.edge_dfs.next(self.package_graph.dep_graph()),
(None, Reverse) => self
.edge_dfs
.next(ReversedDirected(self.package_graph.dep_graph()))
.map(|(source_idx, target_idx, edge_idx)| {
(target_idx, source_idx, edge_idx)
}),
}
}
}
impl<'g> Iterator for IntoIterLinks<'g> {
type Item = DependencyLink<'g>;
fn next(&mut self) -> Option<Self::Item> {
let next_triple = self.next_triple();
next_triple.map(|(source_idx, target_idx, edge_idx)| {
self.package_graph.edge_to_link(
source_idx,
target_idx,
&self.package_graph.dep_graph()[edge_idx],
)
})
}
}
#[derive(Clone, Debug)]
pub(super) enum PackageSelectParams {
All,
TransitiveDeps(Vec<NodeIndex<u32>>),
TransitiveReverseDeps(Vec<NodeIndex<u32>>),
}
impl PackageSelectParams {
fn default_direction(&self) -> DependencyDirection {
match self {
PackageSelectParams::All | PackageSelectParams::TransitiveDeps(_) => {
DependencyDirection::Forward
}
PackageSelectParams::TransitiveReverseDeps(_) => DependencyDirection::Reverse,
}
}
}
fn all_visit_map<G>(graph: G) -> (FixedBitSet, usize)
where
G: Visitable<NodeId = NodeIndex<u32>, Map = FixedBitSet>,
{
let mut visit_map = graph.visit_map();
visit_map.insert_range(..);
let count = visit_map.len();
(visit_map, count)
}
fn reachable_map<G>(graph: G, roots: Vec<G::NodeId>) -> (FixedBitSet, usize)
where
G: Visitable<NodeId = NodeIndex<u32>, Map = FixedBitSet> + IntoNeighbors,
{
let mut visit_map = graph.visit_map();
roots.iter().for_each(|node_idx| {
visit_map.visit(*node_idx);
});
let mut dfs = Dfs::from_parts(roots, visit_map);
while let Some(_) = dfs.next(graph) {}
let reachable = dfs.discovered;
let count = reachable.count_ones(..);
(reachable, count)
}