use crate::{
debug_ignore::DebugIgnore,
graph::{
feature::{FeatureFilter, FeatureSet},
resolve_core::{ResolveCore, Topo},
DependencyDirection, PackageGraph, PackageIx, PackageLink, PackageLinkImpl,
PackageMetadata, PackageQuery,
},
petgraph_support::{
dot::{DotFmt, DotVisitor, DotWrite},
edge_ref::GraphEdgeRef,
IxBitSet,
},
sorted_set::SortedSet,
Error, PackageId,
};
use camino::Utf8Path;
use fixedbitset::FixedBitSet;
use petgraph::{
prelude::*,
visit::{NodeFiltered, NodeRef},
};
use std::fmt;
impl PackageGraph {
pub fn resolve_all(&self) -> PackageSet {
PackageSet {
graph: DebugIgnore(self),
core: ResolveCore::all_nodes(&self.dep_graph),
}
}
pub fn resolve_none(&self) -> PackageSet {
PackageSet {
graph: DebugIgnore(self),
core: ResolveCore::empty(),
}
}
pub fn resolve_ids<'a>(
&self,
package_ids: impl IntoIterator<Item = &'a PackageId>,
) -> Result<PackageSet, Error> {
Ok(PackageSet {
graph: DebugIgnore(self),
core: ResolveCore::from_included::<IxBitSet>(self.package_ixs(package_ids)?),
})
}
pub fn resolve_workspace(&self) -> PackageSet {
let included: IxBitSet = self
.workspace()
.iter_by_path()
.map(|(_, package)| package.package_ix())
.collect();
PackageSet {
graph: DebugIgnore(self),
core: ResolveCore::from_included(included),
}
}
pub fn resolve_workspace_paths(
&self,
paths: impl IntoIterator<Item = impl AsRef<Utf8Path>>,
) -> Result<PackageSet, Error> {
let workspace = self.workspace();
let included: IxBitSet = paths
.into_iter()
.map(|path| {
workspace
.member_by_path(path.as_ref())
.map(|package| package.package_ix())
})
.collect::<Result<_, Error>>()?;
Ok(PackageSet {
graph: DebugIgnore(self),
core: ResolveCore::from_included(included),
})
}
pub fn resolve_workspace_names(
&self,
names: impl IntoIterator<Item = impl AsRef<str>>,
) -> Result<PackageSet, Error> {
let workspace = self.workspace();
let included: IxBitSet = names
.into_iter()
.map(|name| {
workspace
.member_by_name(name.as_ref())
.map(|package| package.package_ix())
})
.collect::<Result<_, _>>()?;
Ok(PackageSet {
graph: DebugIgnore(self),
core: ResolveCore::from_included(included),
})
}
pub fn resolve_package_name(&self, name: impl AsRef<str>) -> PackageSet {
let name = name.as_ref();
let included: IxBitSet = self
.packages()
.filter_map(|package| {
if package.name() == name {
Some(package.package_ix())
} else {
None
}
})
.collect();
PackageSet::from_included(self, included)
}
}
#[derive(Clone, Debug)]
pub struct PackageSet<'g> {
graph: DebugIgnore<&'g PackageGraph>,
core: ResolveCore<PackageGraph>,
}
assert_covariant!(PackageSet);
impl<'g> PackageSet<'g> {
pub(super) fn new(query: PackageQuery<'g>) -> Self {
let graph = query.graph;
Self {
graph: DebugIgnore(graph),
core: ResolveCore::new(graph.dep_graph(), query.params),
}
}
pub(super) fn from_included(graph: &'g PackageGraph, included: impl Into<FixedBitSet>) -> Self {
Self {
graph: DebugIgnore(graph),
core: ResolveCore::from_included(included),
}
}
pub(super) fn with_resolver(
query: PackageQuery<'g>,
mut resolver: impl PackageResolver<'g>,
) -> Self {
let graph = query.graph;
let params = query.params.clone();
Self {
graph: DebugIgnore(graph),
core: ResolveCore::with_edge_filter(graph.dep_graph(), params, |edge| {
let link = graph.edge_ref_to_link(edge);
resolver.accept(&query, link)
}),
}
}
pub fn len(&self) -> usize {
self.core.len()
}
pub fn is_empty(&self) -> bool {
self.core.is_empty()
}
pub fn contains(&self, package_id: &PackageId) -> Result<bool, Error> {
Ok(self.contains_ix(self.graph.package_ix(package_id)?))
}
pub fn to_package_query(&self, direction: DependencyDirection) -> PackageQuery<'g> {
let package_ixs = SortedSet::new(
self.core
.included
.ones()
.map(NodeIndex::new)
.collect::<Vec<_>>(),
);
self.graph.query_from_parts(package_ixs, direction)
}
pub fn union(&self, other: &Self) -> Self {
assert!(
::std::ptr::eq(self.graph.0, other.graph.0),
"package graphs passed into union() match"
);
let mut res = self.clone();
res.core.union_with(&other.core);
res
}
pub fn intersection(&self, other: &Self) -> Self {
assert!(
::std::ptr::eq(self.graph.0, other.graph.0),
"package graphs passed into intersection() match"
);
let mut res = self.clone();
res.core.intersect_with(&other.core);
res
}
pub fn difference(&self, other: &Self) -> Self {
assert!(
::std::ptr::eq(self.graph.0, other.graph.0),
"package graphs passed into difference() match"
);
Self {
graph: self.graph,
core: self.core.difference(&other.core),
}
}
pub fn symmetric_difference(&self, other: &Self) -> Self {
assert!(
::std::ptr::eq(self.graph.0, other.graph.0),
"package graphs passed into symmetric_difference() match"
);
let mut res = self.clone();
res.core.symmetric_difference_with(&other.core);
res
}
pub fn to_feature_set(&self, filter: impl FeatureFilter<'g>) -> FeatureSet<'g> {
let feature_graph = self.graph.feature_graph();
let included: IxBitSet = feature_graph.feature_ixs_for_package_ixs_filtered(
self.ixs(DependencyDirection::Forward),
filter,
);
FeatureSet::from_included(feature_graph, included)
}
pub fn package_ids<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = &'g PackageId> + ExactSizeIterator + 'a {
let graph = self.graph;
self.core
.topo(self.graph.sccs(), direction)
.map(move |package_ix| &graph.dep_graph[package_ix])
}
pub(super) fn ixs(&'g self, direction: DependencyDirection) -> Topo<'g, PackageGraph> {
self.core.topo(self.graph.sccs(), direction)
}
pub fn packages<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = PackageMetadata<'g>> + ExactSizeIterator + 'a {
let graph = self.graph;
self.package_ids(direction)
.map(move |package_id| graph.metadata(package_id).expect("known package IDs"))
}
pub fn root_ids<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = &'g PackageId> + ExactSizeIterator + 'a {
let dep_graph = &self.graph.dep_graph;
self.core
.roots(self.graph.dep_graph(), self.graph.sccs(), direction)
.into_iter()
.map(move |package_ix| &dep_graph[package_ix])
}
pub fn root_packages<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = PackageMetadata<'g>> + ExactSizeIterator + 'a {
let package_graph = self.graph;
self.core
.roots(self.graph.dep_graph(), self.graph.sccs(), direction)
.into_iter()
.map(move |package_ix| {
package_graph
.metadata(&package_graph.dep_graph[package_ix])
.expect("invalid node index")
})
}
pub fn links<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = PackageLink<'g>> + 'a {
let graph = self.graph.0;
self.core
.links(graph.dep_graph(), graph.sccs(), direction)
.map(move |(source_ix, target_ix, edge_ix)| {
PackageLink::new(graph, source_ix, target_ix, edge_ix, None)
})
}
pub fn display_dot<'a, V: PackageDotVisitor + 'g>(
&'a self,
visitor: V,
) -> impl fmt::Display + 'a {
let node_filtered = NodeFiltered(self.graph.dep_graph(), &self.core.included);
DotFmt::new(node_filtered, VisitorWrap::new(self.graph.0, visitor))
}
#[allow(dead_code)]
pub(super) fn ixs_unordered(&self) -> impl Iterator<Item = NodeIndex<PackageIx>> + '_ {
self.core.included.ones().map(NodeIndex::new)
}
pub(super) fn contains_ix(&self, package_ix: NodeIndex<PackageIx>) -> bool {
self.core.contains(package_ix)
}
}
impl<'g> PartialEq for PackageSet<'g> {
fn eq(&self, other: &Self) -> bool {
::std::ptr::eq(self.graph.0, other.graph.0) && self.core == other.core
}
}
impl<'g> Eq for PackageSet<'g> {}
pub trait PackageResolver<'g> {
fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool;
}
impl<'g, 'a, T> PackageResolver<'g> for &'a mut T
where
T: PackageResolver<'g>,
{
fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool {
(**self).accept(query, link)
}
}
impl<'g, 'a> PackageResolver<'g> for Box<dyn PackageResolver<'g> + 'a> {
fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool {
(**self).accept(query, link)
}
}
impl<'g, 'a> PackageResolver<'g> for &'a mut dyn PackageResolver<'g> {
fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool {
(**self).accept(query, link)
}
}
pub(super) struct ResolverFn<F>(pub(super) F);
impl<'g, F> PackageResolver<'g> for ResolverFn<F>
where
F: FnMut(&PackageQuery<'g>, PackageLink<'g>) -> bool,
{
fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool {
(self.0)(query, link)
}
}
pub trait PackageDotVisitor {
fn visit_package(&self, package: PackageMetadata<'_>, f: &mut DotWrite<'_, '_>) -> fmt::Result;
fn visit_link(&self, link: PackageLink<'_>, f: &mut DotWrite<'_, '_>) -> fmt::Result;
}
struct VisitorWrap<'g, V> {
graph: &'g PackageGraph,
inner: V,
}
impl<'g, V> VisitorWrap<'g, V> {
fn new(graph: &'g PackageGraph, inner: V) -> Self {
Self { graph, inner }
}
}
impl<'g, V, NR, ER> DotVisitor<NR, ER> for VisitorWrap<'g, V>
where
V: PackageDotVisitor,
NR: NodeRef<NodeId = NodeIndex<PackageIx>, Weight = PackageId>,
ER: GraphEdgeRef<'g, PackageLinkImpl, PackageIx>,
{
fn visit_node(&self, node: NR, f: &mut DotWrite<'_, '_>) -> fmt::Result {
let metadata = self
.graph
.metadata(node.weight())
.expect("visited node should have associated metadata");
self.inner.visit_package(metadata, f)
}
fn visit_edge(&self, edge: ER, f: &mut DotWrite<'_, '_>) -> fmt::Result {
let link = self.graph.edge_ref_to_link(edge.into_edge_reference());
self.inner.visit_link(link, f)
}
}