1use std::fmt;
5
6use crate::{
7 debug_ignore::DebugIgnore,
8 graph::{
9 cargo::{CargoOptions, CargoSet},
10 feature::{
11 build::FeatureEdgeReference, ConditionalLink, FeatureEdge, FeatureGraph, FeatureId,
12 FeatureList, FeatureMetadata, FeatureQuery, FeatureResolver,
13 },
14 resolve_core::ResolveCore,
15 DependencyDirection, FeatureGraphSpec, FeatureIx, PackageIx, PackageMetadata, PackageSet,
16 },
17 petgraph_support::{dfs::BufferedEdgeFilterFn, IxBitSet},
18 sorted_set::SortedSet,
19 Error, PackageId,
20};
21use fixedbitset::FixedBitSet;
22use itertools::Either;
23use petgraph::{graph::NodeIndex, visit::EdgeRef};
24
25impl<'g> FeatureGraph<'g> {
26 pub fn resolve_all(&self) -> FeatureSet<'g> {
33 FeatureSet {
34 graph: DebugIgnore(*self),
35 core: ResolveCore::all_nodes(self.dep_graph()),
36 }
37 }
38
39 pub fn resolve_none(&self) -> FeatureSet<'g> {
41 FeatureSet {
42 graph: DebugIgnore(*self),
43 core: ResolveCore::empty(),
44 }
45 }
46
47 pub fn resolve_ids<'a>(
51 &self,
52 feature_ids: impl IntoIterator<Item = impl Into<FeatureId<'a>>>,
53 ) -> Result<FeatureSet<'g>, Error> {
54 Ok(FeatureSet {
55 graph: DebugIgnore(*self),
56 core: ResolveCore::from_included::<IxBitSet>(
57 self.feature_ixs(feature_ids.into_iter().map(|feature| feature.into()))?,
58 ),
59 })
60 }
61}
62
63#[derive(Clone)]
68pub struct FeatureSet<'g> {
69 graph: DebugIgnore<FeatureGraph<'g>>,
70 core: ResolveCore<FeatureGraphSpec>,
71}
72
73impl fmt::Debug for FeatureSet<'_> {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 f.debug_set()
76 .entries(self.packages_with_features(DependencyDirection::Forward))
77 .finish()
78 }
79}
80
81assert_covariant!(FeatureSet);
82
83impl<'g> FeatureSet<'g> {
84 pub(super) fn new(query: FeatureQuery<'g>) -> Self {
85 let graph = query.graph;
86 Self {
87 graph,
88 core: ResolveCore::new(graph.dep_graph(), query.params),
89 }
90 }
91
92 pub(super) fn with_resolver(
93 query: FeatureQuery<'g>,
94 mut resolver: impl FeatureResolver<'g>,
95 ) -> Self {
96 let graph = query.graph;
97 let params = query.params.clone();
98
99 let mut buffer_states = graph
101 .inner
102 .weak
103 .new_buffer_states(|link| resolver.accept(&query, link));
104
105 let filter_fn = |edge_ref: FeatureEdgeReference<'g>| {
106 match graph.edge_to_conditional_link(
107 edge_ref.source(),
108 edge_ref.target(),
109 edge_ref.id(),
110 Some(edge_ref.weight()),
111 ) {
112 Some((link, weak_index)) => buffer_states.track(edge_ref, link, weak_index),
113 None => {
114 Either::Left(Some(edge_ref))
116 }
117 }
118 .into_iter()
119 };
120
121 let core = ResolveCore::with_buffered_edge_filter(
122 graph.dep_graph(),
123 params,
124 BufferedEdgeFilterFn(filter_fn),
125 );
126
127 Self { graph, core }
128 }
129
130 #[allow(dead_code)]
131 pub(in crate::graph) fn from_included(
132 graph: FeatureGraph<'g>,
133 included: impl Into<FixedBitSet>,
134 ) -> Self {
135 Self {
136 graph: DebugIgnore(graph),
137 core: ResolveCore::from_included(included.into()),
138 }
139 }
140
141 pub fn graph(&self) -> &FeatureGraph<'g> {
143 &self.graph.0
144 }
145
146 pub fn len(&self) -> usize {
148 self.core.len()
149 }
150
151 pub fn is_empty(&self) -> bool {
153 self.core.is_empty()
154 }
155
156 pub fn contains<'a>(&self, feature_id: impl Into<FeatureId<'a>>) -> Result<bool, Error> {
160 Ok(self
161 .core
162 .contains(self.graph.feature_ix(feature_id.into())?))
163 }
164
165 pub fn contains_package(&self, package_id: &PackageId) -> Result<bool, Error> {
169 let package = self.graph.package_graph.metadata(package_id)?;
170 Ok(self
171 .graph
172 .feature_ixs_for_package_ix(package.package_ix())
173 .any(|feature_ix| self.core.contains(feature_ix)))
174 }
175
176 pub fn to_feature_query(&self, direction: DependencyDirection) -> FeatureQuery<'g> {
180 let feature_ixs = SortedSet::new(
181 self.core
182 .included
183 .ones()
184 .map(NodeIndex::new)
185 .collect::<Vec<_>>(),
186 );
187 self.graph.query_from_parts(feature_ixs, direction)
188 }
189
190 pub fn union(&self, other: &Self) -> Self {
201 assert!(
202 ::std::ptr::eq(self.graph.package_graph, self.graph.package_graph),
203 "package graphs passed into union() match"
204 );
205 let mut res = self.clone();
206 res.core.union_with(&other.core);
207 res
208 }
209
210 pub fn intersection(&self, other: &Self) -> Self {
216 assert!(
217 ::std::ptr::eq(self.graph.package_graph, self.graph.package_graph),
218 "package graphs passed into intersection() match"
219 );
220 let mut res = self.clone();
221 res.core.intersect_with(&other.core);
222 res
223 }
224
225 pub fn difference(&self, other: &Self) -> Self {
231 assert!(
232 ::std::ptr::eq(self.graph.package_graph, self.graph.package_graph),
233 "package graphs passed into difference() match"
234 );
235 Self {
236 graph: self.graph,
237 core: self.core.difference(&other.core),
238 }
239 }
240
241 pub fn symmetric_difference(&self, other: &Self) -> Self {
248 assert!(
249 ::std::ptr::eq(self.graph.package_graph, self.graph.package_graph),
250 "package graphs passed into symmetric_difference() match"
251 );
252 let mut res = self.clone();
253 res.core.symmetric_difference_with(&other.core);
254 res
255 }
256
257 pub fn filter(
267 &self,
268 direction: DependencyDirection,
269 mut callback: impl FnMut(FeatureMetadata<'g>) -> bool,
270 ) -> Self {
271 let graph = self.graph;
272 let included: IxBitSet = self
273 .features(direction)
274 .filter_map(move |feature| {
275 let feature_ix = feature.feature_ix();
276 if callback(feature) {
277 Some(feature_ix)
278 } else {
279 None
280 }
281 })
282 .collect();
283 Self::from_included(*graph, included)
284 }
285
286 pub fn partition(
297 &self,
298 direction: DependencyDirection,
299 mut callback: impl FnMut(FeatureMetadata<'g>) -> bool,
300 ) -> (Self, Self) {
301 let graph = self.graph;
302 let mut left = IxBitSet::with_capacity(self.core.included.len());
303 let mut right = left.clone();
304
305 self.features(direction).for_each(|feature| {
306 let feature_ix = feature.feature_ix();
307 match callback(feature) {
308 true => left.insert_node_ix(feature_ix),
309 false => right.insert_node_ix(feature_ix),
310 }
311 });
312 (
313 Self::from_included(*graph, left),
314 Self::from_included(*graph, right),
315 )
316 }
317
318 pub fn filter_partition(
330 &self,
331 direction: DependencyDirection,
332 mut callback: impl FnMut(FeatureMetadata<'g>) -> Option<bool>,
333 ) -> (Self, Self) {
334 let graph = self.graph;
335 let mut left = IxBitSet::with_capacity(self.core.included.len());
336 let mut right = left.clone();
337
338 self.features(direction).for_each(|feature| {
339 let feature_ix = feature.feature_ix();
340 match callback(feature) {
341 Some(true) => left.insert_node_ix(feature_ix),
342 Some(false) => right.insert_node_ix(feature_ix),
343 None => {}
344 }
345 });
346 (
347 Self::from_included(*graph, left),
348 Self::from_included(*graph, right),
349 )
350 }
351
352 pub fn features_for(&self, package_id: &PackageId) -> Result<Option<FeatureList<'g>>, Error> {
361 let package = self.graph.package_graph.metadata(package_id)?;
362 Ok(self.features_for_package_impl(package))
363 }
364
365 pub fn to_package_set(&self) -> PackageSet<'g> {
368 let included: IxBitSet = self
369 .core
370 .included
371 .ones()
372 .map(|feature_ix| {
373 self.graph
374 .package_ix_for_feature_ix(NodeIndex::new(feature_ix))
375 })
376 .collect();
377 PackageSet::from_included(self.graph.package_graph, included.0)
378 }
379
380 pub fn into_cargo_set(self, opts: &CargoOptions<'_>) -> Result<CargoSet<'g>, Error> {
392 let features_only = self.graph.resolve_none();
393 CargoSet::new(self, features_only, opts)
394 }
395
396 pub fn feature_ids<'a>(
408 &'a self,
409 direction: DependencyDirection,
410 ) -> impl ExactSizeIterator<Item = FeatureId<'g>> + 'a {
411 let graph = self.graph;
412 self.core
413 .topo(graph.sccs(), direction)
414 .map(move |feature_ix| {
415 FeatureId::from_node(graph.package_graph(), &graph.dep_graph()[feature_ix])
416 })
417 }
418
419 pub fn features<'a>(
427 &'a self,
428 direction: DependencyDirection,
429 ) -> impl ExactSizeIterator<Item = FeatureMetadata<'g>> + 'a {
430 let graph = self.graph;
431 self.core
432 .topo(graph.sccs(), direction)
433 .map(move |feature_ix| {
434 graph
435 .metadata_for_node(graph.dep_graph()[feature_ix])
436 .expect("feature node should be known")
437 })
438 }
439
440 pub fn packages_with_features<'a>(
449 &'a self,
450 direction: DependencyDirection,
451 ) -> impl Iterator<Item = FeatureList<'g>> + 'a {
452 let package_graph = self.graph.package_graph;
453
454 package_graph
456 .sccs()
457 .node_iter(direction.into())
458 .filter_map(move |package_ix| {
459 let package_id = &package_graph.dep_graph()[package_ix];
460 let package = package_graph
461 .metadata(package_id)
462 .expect("valid package ID");
463 self.features_for_package_impl(package)
464 })
465 }
466
467 pub fn root_ids<'a>(
479 &'a self,
480 direction: DependencyDirection,
481 ) -> impl ExactSizeIterator<Item = FeatureId<'g>> + 'a {
482 let dep_graph = self.graph.dep_graph();
483 let package_graph = self.graph.package_graph;
484 self.core
485 .roots(dep_graph, self.graph.sccs(), direction)
486 .into_iter()
487 .map(move |feature_ix| FeatureId::from_node(package_graph, &dep_graph[feature_ix]))
488 }
489
490 pub fn root_features<'a>(
502 &'a self,
503 direction: DependencyDirection,
504 ) -> impl Iterator<Item = FeatureMetadata<'g>> + 'a {
505 let feature_graph = self.graph;
506 self.core
507 .roots(feature_graph.dep_graph(), feature_graph.sccs(), direction)
508 .into_iter()
509 .map(move |feature_ix| {
510 feature_graph
511 .metadata_for_node(feature_graph.dep_graph()[feature_ix])
512 .expect("feature node should be known")
513 })
514 }
515
516 pub fn conditional_links<'a>(
524 &'a self,
525 direction: DependencyDirection,
526 ) -> impl Iterator<Item = ConditionalLink<'g>> + 'a {
527 let graph = self.graph;
528 self.core
529 .links(graph.dep_graph(), graph.sccs(), direction)
530 .filter_map(move |(source_ix, target_ix, edge_ix)| {
531 graph
532 .edge_to_conditional_link(source_ix, target_ix, edge_ix, None)
533 .map(|(link, _)| link)
534 })
535 }
536
537 fn features_for_package_impl<'a>(
542 &'a self,
543 package: PackageMetadata<'g>,
544 ) -> Option<FeatureList<'g>> {
545 let dep_graph = self.graph.dep_graph();
546 let core = &self.core;
547
548 let mut features = self
549 .graph
550 .feature_ixs_for_package_ix(package.package_ix())
551 .filter_map(|feature_ix| {
552 if core.contains(feature_ix) {
553 Some(FeatureId::node_to_feature(package, &dep_graph[feature_ix]))
554 } else {
555 None
556 }
557 })
558 .peekable();
559 if features.peek().is_some() {
560 Some(FeatureList::new(package, features))
562 } else {
563 None
564 }
565 }
566
567 pub(in crate::graph) fn ixs_unordered(
569 &self,
570 ) -> impl Iterator<Item = NodeIndex<FeatureIx>> + '_ {
571 self.core.included.ones().map(NodeIndex::new)
572 }
573
574 #[allow(dead_code)]
576 pub(in crate::graph) fn contains_package_ix(&self, package_ix: NodeIndex<PackageIx>) -> bool {
577 self.graph
578 .feature_ixs_for_package_ix(package_ix)
579 .any(|feature_ix| self.core.contains(feature_ix))
580 }
581
582 #[doc(hidden)]
584 pub fn links<'a>(
585 &'a self,
586 direction: DependencyDirection,
587 ) -> impl Iterator<Item = (FeatureId<'g>, FeatureId<'g>, &'g FeatureEdge)> + 'a {
588 let feature_graph = self.graph;
589
590 self.core
591 .links(feature_graph.dep_graph(), feature_graph.sccs(), direction)
592 .map(move |(source_ix, target_ix, edge_ix)| {
593 (
594 FeatureId::from_node(
595 feature_graph.package_graph(),
596 &feature_graph.dep_graph()[source_ix],
597 ),
598 FeatureId::from_node(
599 feature_graph.package_graph(),
600 &feature_graph.dep_graph()[target_ix],
601 ),
602 &feature_graph.dep_graph()[edge_ix],
603 )
604 })
605 }
606}
607
608impl PartialEq for FeatureSet<'_> {
609 fn eq(&self, other: &Self) -> bool {
610 ::std::ptr::eq(self.graph.package_graph, other.graph.package_graph)
611 && self.core == other.core
612 }
613}
614
615impl Eq for FeatureSet<'_> {}