1use std::fmt;
5
6use crate::{
7    Error, PackageId,
8    debug_ignore::DebugIgnore,
9    graph::{
10        DependencyDirection, FeatureGraphSpec, FeatureIx, PackageIx, PackageMetadata, PackageSet,
11        cargo::{CargoOptions, CargoSet},
12        feature::{
13            ConditionalLink, FeatureEdge, FeatureGraph, FeatureId, FeatureList, FeatureMetadata,
14            FeatureQuery, FeatureResolver, build::FeatureEdgeReference,
15        },
16        resolve_core::ResolveCore,
17    },
18    petgraph_support::{IxBitSet, dfs::BufferedEdgeFilterFn},
19    sorted_set::SortedSet,
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<'_> {}