1use std::fmt;
5
6use crate::{
7 Error, PackageId,
8 graph::{
9 DependencyDirection, FeatureGraphSpec, FeatureIx, PackageIx, PackageMetadata, PackageSet,
10 cargo::{CargoOptions, CargoSet},
11 feature::{
12 ConditionalLink, FeatureEdge, FeatureGraph, FeatureId, FeatureLinkContext,
13 FeatureLinkVisitor, FeatureList, FeatureMetadata, FeatureQuery,
14 build::{FeatureEdgeReference, FeaturePetgraph},
15 weak::WeakBufferStates,
16 },
17 resolve_core::ResolveCore,
18 },
19 petgraph_support::{IxBitSet, dfs::BufferedEdgeFilter},
20};
21use debug_ignore::DebugIgnore;
22use fixedbitset::FixedBitSet;
23use itertools::Either;
24use petgraph::{graph::NodeIndex, visit::EdgeRef};
25
26impl<'g> FeatureGraph<'g> {
27 pub fn resolve_all(&self) -> FeatureSet<'g> {
34 FeatureSet {
35 graph: DebugIgnore(*self),
36 core: ResolveCore::all_nodes(self.dep_graph()),
37 }
38 }
39
40 pub fn resolve_none(&self) -> FeatureSet<'g> {
42 FeatureSet {
43 graph: DebugIgnore(*self),
44 core: ResolveCore::empty(self.dep_graph()),
45 }
46 }
47
48 pub fn resolve_ids<'a>(
52 &self,
53 feature_ids: impl IntoIterator<Item = impl Into<FeatureId<'a>>>,
54 ) -> Result<FeatureSet<'g>, Error> {
55 let included: IxBitSet =
56 self.feature_ixs(feature_ids.into_iter().map(|feature| feature.into()))?;
57 Ok(FeatureSet::from_included(*self, included))
58 }
59}
60
61#[derive(Clone)]
66pub struct FeatureSet<'g> {
67 graph: DebugIgnore<FeatureGraph<'g>>,
68 core: ResolveCore<FeatureGraphSpec>,
69}
70
71impl fmt::Debug for FeatureSet<'_> {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 f.debug_set()
74 .entries(self.packages_with_features(DependencyDirection::Forward))
75 .finish()
76 }
77}
78
79assert_covariant!(FeatureSet);
80
81struct FeatureEdgeFilter<'g, 'a, F> {
82 graph: FeatureGraph<'g>,
83 buffer_states: WeakBufferStates<'g, 'a, F>,
84}
85
86impl<'g, F> BufferedEdgeFilter<&'g FeaturePetgraph> for FeatureEdgeFilter<'g, '_, F>
87where
88 F: FnMut(ConditionalLink<'g>) -> bool,
89{
90 type Iter = Either<
91 std::option::IntoIter<FeatureEdgeReference<'g>>,
92 std::vec::IntoIter<FeatureEdgeReference<'g>>,
93 >;
94
95 fn filter(&mut self, edge_ref: FeatureEdgeReference<'g>) -> Self::Iter {
96 match self.graph.edge_to_links(
97 edge_ref.source(),
98 edge_ref.target(),
99 edge_ref.id(),
100 Some(edge_ref.weight()),
101 ) {
102 Some(links) => Either::Left(self.buffer_states.track(edge_ref, links).into_iter()),
103 None => {
104 Either::Left(Some(edge_ref).into_iter())
106 }
107 }
108 }
109
110 fn discover(&mut self, feature_ix: NodeIndex<FeatureIx>) -> Self::Iter {
111 Either::Right(self.buffer_states.discover(feature_ix).into_iter())
112 }
113}
114
115impl<'g> FeatureSet<'g> {
116 pub(super) fn new(query: FeatureQuery<'g>) -> Self {
117 let graph = query.initials.graph;
118 Self {
119 graph,
120 core: ResolveCore::new(
121 graph.dep_graph(),
122 query.initials().sorted_ixs(),
123 query.direction,
124 ),
125 }
126 }
127
128 pub(super) fn with_link_visitor(
129 query: FeatureQuery<'g>,
130 mut visitor: impl FeatureLinkVisitor<'g>,
131 ) -> Self {
132 let graph = query.initials.graph;
133 let cx = FeatureLinkContext::new(query);
134
135 let buffer_states = WeakBufferStates::new(&graph.inner.weak, cx.direction(), |link| {
137 visitor.visit_link(&cx, link)
138 });
139
140 let core = ResolveCore::with_buffered_edge_filter(
141 graph.dep_graph(),
142 cx.query().initials().sorted_ixs(),
143 cx.direction(),
144 FeatureEdgeFilter {
145 graph: *graph,
146 buffer_states,
147 },
148 );
149
150 Self { graph, core }
151 }
152
153 pub(super) fn from_ixs(
154 graph: FeatureGraph<'g>,
155 feature_ixs: impl IntoIterator<Item = NodeIndex<FeatureIx>>,
156 ) -> Self {
157 Self {
158 graph: DebugIgnore(graph),
159 core: ResolveCore::from_ixs(feature_ixs, graph.dep_graph()),
160 }
161 }
162
163 pub(in crate::graph) fn from_included(
164 graph: FeatureGraph<'g>,
165 included: impl Into<FixedBitSet>,
166 ) -> Self {
167 Self {
168 graph: DebugIgnore(graph),
169 core: ResolveCore::from_included(included.into(), graph.dep_graph()),
170 }
171 }
172
173 pub fn graph(&self) -> &FeatureGraph<'g> {
175 &self.graph.0
176 }
177
178 pub fn len(&self) -> usize {
180 self.core.len()
181 }
182
183 pub fn is_empty(&self) -> bool {
185 self.core.is_empty()
186 }
187
188 pub fn contains<'a>(&self, feature_id: impl Into<FeatureId<'a>>) -> Result<bool, Error> {
192 Ok(self.contains_ix(self.graph.feature_ix(feature_id.into())?))
193 }
194
195 pub fn contains_package(&self, package_id: &PackageId) -> Result<bool, Error> {
199 let package = self.graph.package_graph.metadata(package_id)?;
200 Ok(self
201 .graph
202 .feature_ixs_for_package_ix(package.package_ix())
203 .any(|feature_ix| self.core.contains(feature_ix)))
204 }
205
206 pub fn to_feature_query(&self, direction: DependencyDirection) -> FeatureQuery<'g> {
210 FeatureQuery {
211 initials: self.clone(),
212 direction,
213 }
214 }
215
216 pub fn union(&self, other: &Self) -> Self {
227 assert!(
228 ::std::ptr::eq(self.graph.package_graph, other.graph.package_graph),
229 "package graphs passed into union() match"
230 );
231 let mut res = self.clone();
232 res.core.union_with(&other.core);
233 res
234 }
235
236 pub fn intersection(&self, other: &Self) -> Self {
242 assert!(
243 ::std::ptr::eq(self.graph.package_graph, other.graph.package_graph),
244 "package graphs passed into intersection() match"
245 );
246 let mut res = self.clone();
247 res.core.intersect_with(&other.core);
248 res
249 }
250
251 pub fn difference(&self, other: &Self) -> Self {
257 assert!(
258 ::std::ptr::eq(self.graph.package_graph, other.graph.package_graph),
259 "package graphs passed into difference() match"
260 );
261 Self {
262 graph: self.graph,
263 core: self.core.difference(&other.core),
264 }
265 }
266
267 pub fn symmetric_difference(&self, other: &Self) -> Self {
274 assert!(
275 ::std::ptr::eq(self.graph.package_graph, other.graph.package_graph),
276 "package graphs passed into symmetric_difference() match"
277 );
278 let mut res = self.clone();
279 res.core.symmetric_difference_with(&other.core);
280 res
281 }
282
283 pub fn filter(
293 &self,
294 direction: DependencyDirection,
295 mut callback: impl FnMut(FeatureMetadata<'g>) -> bool,
296 ) -> Self {
297 let graph = self.graph;
298 let included: IxBitSet = self
299 .features(direction)
300 .filter_map(move |feature| {
301 let feature_ix = feature.feature_ix();
302 if callback(feature) {
303 Some(feature_ix)
304 } else {
305 None
306 }
307 })
308 .collect();
309 Self::from_included(*graph, included)
310 }
311
312 pub fn partition(
323 &self,
324 direction: DependencyDirection,
325 mut callback: impl FnMut(FeatureMetadata<'g>) -> bool,
326 ) -> (Self, Self) {
327 let graph = self.graph;
328 let mut left = IxBitSet::with_capacity(graph.dep_graph().node_count());
329 let mut right = left.clone();
330
331 self.features(direction).for_each(|feature| {
332 let feature_ix = feature.feature_ix();
333 match callback(feature) {
334 true => left.insert_node_ix(feature_ix),
335 false => right.insert_node_ix(feature_ix),
336 }
337 });
338 (
339 Self::from_included(*graph, left),
340 Self::from_included(*graph, right),
341 )
342 }
343
344 pub fn filter_partition(
356 &self,
357 direction: DependencyDirection,
358 mut callback: impl FnMut(FeatureMetadata<'g>) -> Option<bool>,
359 ) -> (Self, Self) {
360 let graph = self.graph;
361 let mut left = IxBitSet::with_capacity(graph.dep_graph().node_count());
362 let mut right = left.clone();
363
364 self.features(direction).for_each(|feature| {
365 let feature_ix = feature.feature_ix();
366 match callback(feature) {
367 Some(true) => left.insert_node_ix(feature_ix),
368 Some(false) => right.insert_node_ix(feature_ix),
369 None => {}
370 }
371 });
372 (
373 Self::from_included(*graph, left),
374 Self::from_included(*graph, right),
375 )
376 }
377
378 pub fn features_for(&self, package_id: &PackageId) -> Result<Option<FeatureList<'g>>, Error> {
387 let package = self.graph.package_graph.metadata(package_id)?;
388 Ok(self.features_for_package_impl(package))
389 }
390
391 pub fn to_package_set(&self) -> PackageSet<'g> {
394 let included: IxBitSet = self
395 .core
396 .included
397 .ones()
398 .map(|feature_ix| self.graph.package_ix_for_feature_ix(feature_ix))
399 .collect();
400 PackageSet::from_included(self.graph.package_graph, included.0)
401 }
402
403 pub fn into_cargo_set(self, opts: &CargoOptions<'_>) -> Result<CargoSet<'g>, Error> {
415 let features_only = self.graph.resolve_none();
416 CargoSet::new(self, features_only, opts)
417 }
418
419 pub fn feature_ids<'a>(
431 &'a self,
432 direction: DependencyDirection,
433 ) -> impl ExactSizeIterator<Item = FeatureId<'g>> + 'a {
434 let graph = self.graph;
435 self.core
436 .topo(graph.sccs(), direction)
437 .map(move |feature_ix| {
438 FeatureId::from_node(graph.package_graph(), &graph.dep_graph()[feature_ix])
439 })
440 }
441
442 pub fn features<'a>(
450 &'a self,
451 direction: DependencyDirection,
452 ) -> impl ExactSizeIterator<Item = FeatureMetadata<'g>> + 'a {
453 let graph = self.graph;
454 self.core
455 .topo(graph.sccs(), direction)
456 .map(move |feature_ix| {
457 graph
458 .metadata_for_node(graph.dep_graph()[feature_ix])
459 .expect("feature node should be known")
460 })
461 }
462
463 pub fn packages_with_features<'a>(
472 &'a self,
473 direction: DependencyDirection,
474 ) -> impl Iterator<Item = FeatureList<'g>> + 'a {
475 let package_graph = self.graph.package_graph;
476
477 package_graph
479 .sccs()
480 .node_iter(direction.into())
481 .filter_map(move |package_ix| {
482 let package_id = &package_graph.dep_graph()[package_ix];
483 let package = package_graph
484 .metadata(package_id)
485 .expect("valid package ID");
486 self.features_for_package_impl(package)
487 })
488 }
489
490 pub fn root_ids<'a>(
502 &'a self,
503 direction: DependencyDirection,
504 ) -> impl ExactSizeIterator<Item = FeatureId<'g>> + 'a {
505 let dep_graph = self.graph.dep_graph();
506 let package_graph = self.graph.package_graph;
507 self.core
508 .roots(dep_graph, self.graph.sccs(), direction)
509 .into_iter()
510 .map(move |feature_ix| FeatureId::from_node(package_graph, &dep_graph[feature_ix]))
511 }
512
513 pub fn root_features<'a>(
525 &'a self,
526 direction: DependencyDirection,
527 ) -> impl Iterator<Item = FeatureMetadata<'g>> + 'a {
528 let feature_graph = self.graph;
529 self.core
530 .roots(feature_graph.dep_graph(), feature_graph.sccs(), direction)
531 .into_iter()
532 .map(move |feature_ix| {
533 feature_graph
534 .metadata_for_node(feature_graph.dep_graph()[feature_ix])
535 .expect("feature node should be known")
536 })
537 }
538
539 pub fn conditional_links<'a>(
547 &'a self,
548 direction: DependencyDirection,
549 ) -> impl Iterator<Item = ConditionalLink<'g>> + 'a {
550 let graph = self.graph;
551 self.core
552 .links(graph.dep_graph(), graph.sccs(), direction)
553 .filter_map(move |(source_ix, target_ix, edge_ix)| {
554 graph.edge_to_full_link(source_ix, target_ix, edge_ix, None)
555 })
556 }
557
558 fn features_for_package_impl<'a>(
563 &'a self,
564 package: PackageMetadata<'g>,
565 ) -> Option<FeatureList<'g>> {
566 let dep_graph = self.graph.dep_graph();
567 let core = &self.core;
568
569 let mut features = self
570 .graph
571 .feature_ixs_for_package_ix(package.package_ix())
572 .filter_map(|feature_ix| {
573 if core.contains(feature_ix) {
574 Some(FeatureId::node_to_feature(package, &dep_graph[feature_ix]))
575 } else {
576 None
577 }
578 })
579 .peekable();
580 if features.peek().is_some() {
581 Some(FeatureList::new(package, features))
583 } else {
584 None
585 }
586 }
587
588 pub(in crate::graph) fn sorted_ixs(&self) -> impl Iterator<Item = NodeIndex<FeatureIx>> + '_ {
590 self.core.included.ones()
591 }
592
593 pub(in crate::graph) fn contains_ix(&self, feature_ix: NodeIndex<FeatureIx>) -> bool {
594 self.core.contains(feature_ix)
595 }
596
597 #[allow(dead_code)]
599 pub(in crate::graph) fn contains_package_ix(&self, package_ix: NodeIndex<PackageIx>) -> bool {
600 self.graph
601 .feature_ixs_for_package_ix(package_ix)
602 .any(|feature_ix| self.core.contains(feature_ix))
603 }
604
605 #[doc(hidden)]
607 pub fn links<'a>(
608 &'a self,
609 direction: DependencyDirection,
610 ) -> impl Iterator<Item = (FeatureId<'g>, FeatureId<'g>, &'g FeatureEdge)> + 'a {
611 let feature_graph = self.graph;
612
613 self.core
614 .links(feature_graph.dep_graph(), feature_graph.sccs(), direction)
615 .map(move |(source_ix, target_ix, edge_ix)| {
616 (
617 FeatureId::from_node(
618 feature_graph.package_graph(),
619 &feature_graph.dep_graph()[source_ix],
620 ),
621 FeatureId::from_node(
622 feature_graph.package_graph(),
623 &feature_graph.dep_graph()[target_ix],
624 ),
625 &feature_graph.dep_graph()[edge_ix],
626 )
627 })
628 }
629}
630
631impl PartialEq for FeatureSet<'_> {
632 fn eq(&self, other: &Self) -> bool {
633 ::std::ptr::eq(self.graph.package_graph, other.graph.package_graph)
634 && self.core == other.core
635 }
636}
637
638impl Eq for FeatureSet<'_> {}