use crate::{
debug_ignore::DebugIgnore,
graph::{
cargo::{CargoOptions, CargoSet},
feature::{
CrossLink, FeatureEdge, FeatureGraph, FeatureId, FeatureList, FeatureMetadata,
FeatureQuery, FeatureResolver,
},
resolve_core::ResolveCore,
DependencyDirection, FeatureGraphSpec, FeatureIx, PackageIx, PackageMetadata, PackageSet,
},
petgraph_support::IxBitSet,
sorted_set::SortedSet,
Error, PackageId,
};
use fixedbitset::FixedBitSet;
use petgraph::{graph::NodeIndex, visit::EdgeRef};
impl<'g> FeatureGraph<'g> {
pub fn resolve_all(&self) -> FeatureSet<'g> {
FeatureSet {
graph: DebugIgnore(*self),
core: ResolveCore::all_nodes(self.dep_graph()),
}
}
pub fn resolve_none(&self) -> FeatureSet<'g> {
FeatureSet {
graph: DebugIgnore(*self),
core: ResolveCore::empty(),
}
}
pub fn resolve_ids<'a>(
&self,
feature_ids: impl IntoIterator<Item = impl Into<FeatureId<'a>>>,
) -> Result<FeatureSet<'g>, Error> {
Ok(FeatureSet {
graph: DebugIgnore(*self),
core: ResolveCore::from_included::<IxBitSet>(
self.feature_ixs(feature_ids.into_iter().map(|feature| feature.into()))?,
),
})
}
}
#[derive(Clone, Debug)]
pub struct FeatureSet<'g> {
graph: DebugIgnore<FeatureGraph<'g>>,
core: ResolveCore<FeatureGraphSpec>,
}
assert_covariant!(FeatureSet);
impl<'g> FeatureSet<'g> {
pub(super) fn new(query: FeatureQuery<'g>) -> Self {
let graph = query.graph;
Self {
graph: DebugIgnore(graph),
core: ResolveCore::new(graph.dep_graph(), query.params),
}
}
pub(super) fn with_resolver(
query: FeatureQuery<'g>,
mut resolver: impl FeatureResolver<'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| {
match graph.edge_to_cross_link(
edge.source(),
edge.target(),
edge.id(),
Some(edge.weight()),
) {
Some(cross_link) => resolver.accept(&query, cross_link),
None => {
true
}
}
}),
}
}
#[allow(dead_code)]
pub(in crate::graph) fn from_included(
graph: FeatureGraph<'g>,
included: impl Into<FixedBitSet>,
) -> Self {
Self {
graph: DebugIgnore(graph),
core: ResolveCore::from_included(included.into()),
}
}
pub fn graph(&self) -> &FeatureGraph<'g> {
&self.graph.0
}
pub fn len(&self) -> usize {
self.core.len()
}
pub fn is_empty(&self) -> bool {
self.core.is_empty()
}
pub fn contains<'a>(&self, feature_id: impl Into<FeatureId<'a>>) -> Result<bool, Error> {
Ok(self
.core
.contains(self.graph.feature_ix(feature_id.into())?))
}
pub fn contains_package(&self, package_id: &PackageId) -> Result<bool, Error> {
let package = self.graph.package_graph.metadata(package_id)?;
Ok(self
.graph
.feature_ixs_for_package_ix(package.package_ix())
.any(|feature_ix| self.core.contains(feature_ix)))
}
pub fn to_feature_query(&self, direction: DependencyDirection) -> FeatureQuery<'g> {
let feature_ixs = SortedSet::new(
self.core
.included
.ones()
.map(NodeIndex::new)
.collect::<Vec<_>>(),
);
self.graph.query_from_parts(feature_ixs, direction)
}
pub fn union(&self, other: &Self) -> Self {
assert!(
::std::ptr::eq(self.graph.package_graph, self.graph.package_graph),
"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.package_graph, self.graph.package_graph),
"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.package_graph, self.graph.package_graph),
"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.package_graph, self.graph.package_graph),
"package graphs passed into symmetric_difference() match"
);
let mut res = self.clone();
res.core.symmetric_difference_with(&other.core);
res
}
pub fn features_for(&self, package_id: &PackageId) -> Result<Option<FeatureList<'g>>, Error> {
let package = self.graph.package_graph.metadata(package_id)?;
Ok(self.features_for_package_impl(package))
}
pub fn to_package_set(&self) -> PackageSet<'g> {
let included: IxBitSet = self
.core
.included
.ones()
.map(|feature_ix| {
self.graph
.package_ix_for_feature_ix(NodeIndex::new(feature_ix))
})
.collect();
PackageSet::from_included(self.graph.package_graph, included.0)
}
pub fn into_cargo_set(self, opts: &CargoOptions<'_>) -> Result<CargoSet<'g>, Error> {
let features_only = self.graph.resolve_none();
CargoSet::new(self, features_only, opts)
}
pub fn feature_ids<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = FeatureId<'g>> + ExactSizeIterator + 'a {
let graph = self.graph;
self.core
.topo(graph.sccs(), direction)
.map(move |feature_ix| {
FeatureId::from_node(graph.package_graph(), &graph.dep_graph()[feature_ix])
})
}
pub fn features<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = FeatureMetadata<'g>> + ExactSizeIterator + 'a {
let graph = self.graph;
self.core
.topo(graph.sccs(), direction)
.map(move |feature_ix| {
graph
.metadata_for_node(graph.dep_graph()[feature_ix])
.expect("feature node should be known")
})
}
pub fn packages_with_features<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = FeatureList<'g>> + 'a {
let package_graph = self.graph.package_graph;
package_graph
.sccs()
.node_iter(direction.into())
.filter_map(move |package_ix| {
let package_id = &package_graph.dep_graph()[package_ix];
let package = package_graph
.metadata(package_id)
.expect("valid package ID");
self.features_for_package_impl(package)
})
}
pub fn root_ids<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = FeatureId<'g>> + ExactSizeIterator + 'a {
let dep_graph = self.graph.dep_graph();
let package_graph = self.graph.package_graph;
self.core
.roots(dep_graph, self.graph.sccs(), direction)
.into_iter()
.map(move |feature_ix| FeatureId::from_node(package_graph, &dep_graph[feature_ix]))
}
pub fn root_features<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = FeatureMetadata<'g>> + 'a {
let feature_graph = self.graph;
self.core
.roots(feature_graph.dep_graph(), feature_graph.sccs(), direction)
.into_iter()
.map(move |feature_ix| {
feature_graph
.metadata_for_node(feature_graph.dep_graph()[feature_ix])
.expect("feature node should be known")
})
}
pub fn cross_links<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = CrossLink<'g>> + 'a {
let graph = self.graph;
self.core
.links(graph.dep_graph(), graph.sccs(), direction)
.filter_map(move |(source_ix, target_ix, edge_ix)| {
graph.edge_to_cross_link(source_ix, target_ix, edge_ix, None)
})
}
fn features_for_package_impl<'a>(
&'a self,
package: PackageMetadata<'g>,
) -> Option<FeatureList<'g>> {
let dep_graph = self.graph.dep_graph();
let core = &self.core;
let mut features = self
.graph
.feature_ixs_for_package_ix(package.package_ix())
.filter_map(|feature_ix| {
if core.contains(feature_ix) {
Some(FeatureId::node_to_feature(package, &dep_graph[feature_ix]))
} else {
None
}
})
.peekable();
if features.peek().is_some() {
Some(FeatureList::new(package, features))
} else {
None
}
}
pub(in crate::graph) fn ixs_unordered(
&self,
) -> impl Iterator<Item = NodeIndex<FeatureIx>> + '_ {
self.core.included.ones().map(NodeIndex::new)
}
#[allow(dead_code)]
pub(in crate::graph) fn contains_package_ix(&self, package_ix: NodeIndex<PackageIx>) -> bool {
self.graph
.feature_ixs_for_package_ix(package_ix)
.any(|feature_ix| self.core.contains(feature_ix))
}
#[doc(hidden)]
pub fn links<'a>(
&'a self,
direction: DependencyDirection,
) -> impl Iterator<Item = (FeatureId<'g>, FeatureId<'g>, &'g FeatureEdge)> + 'a {
let feature_graph = self.graph;
self.core
.links(feature_graph.dep_graph(), feature_graph.sccs(), direction)
.map(move |(source_ix, target_ix, edge_ix)| {
(
FeatureId::from_node(
feature_graph.package_graph(),
&feature_graph.dep_graph()[source_ix],
),
FeatureId::from_node(
feature_graph.package_graph(),
&feature_graph.dep_graph()[target_ix],
),
&feature_graph.dep_graph()[edge_ix],
)
})
}
}
impl<'g> PartialEq for FeatureSet<'g> {
fn eq(&self, other: &Self) -> bool {
::std::ptr::eq(self.graph.package_graph, other.graph.package_graph)
&& self.core == other.core
}
}
impl<'g> Eq for FeatureSet<'g> {}