1use crate::{
5 debug_ignore::DebugIgnore,
6 graph::{
7 feature::{
8 ConditionalLink, FeatureGraph, FeatureId, FeatureLabel, FeatureMetadata, FeatureSet,
9 },
10 query_core::QueryParams,
11 DependencyDirection, FeatureGraphSpec, FeatureIx, PackageIx, PackageMetadata,
12 },
13 sorted_set::SortedSet,
14 Error, PackageId,
15};
16use itertools::Itertools;
17use petgraph::graph::NodeIndex;
18use std::collections::HashSet;
19
20pub trait FeatureFilter<'g> {
29 fn accept(&mut self, graph: &FeatureGraph<'g>, feature_id: FeatureId<'g>) -> bool;
38}
39
40impl<'g, T> FeatureFilter<'g> for &mut T
41where
42 T: FeatureFilter<'g>,
43{
44 fn accept(&mut self, graph: &FeatureGraph<'g>, feature_id: FeatureId<'g>) -> bool {
45 (**self).accept(graph, feature_id)
46 }
47}
48
49impl<'g> FeatureFilter<'g> for Box<dyn FeatureFilter<'g> + '_> {
50 fn accept(&mut self, graph: &FeatureGraph<'g>, feature_id: FeatureId<'g>) -> bool {
51 (**self).accept(graph, feature_id)
52 }
53}
54
55impl<'g> FeatureFilter<'g> for &mut dyn FeatureFilter<'g> {
56 fn accept(&mut self, graph: &FeatureGraph<'g>, feature_id: FeatureId<'g>) -> bool {
57 (**self).accept(graph, feature_id)
58 }
59}
60
61#[derive(Clone, Debug)]
63pub struct FeatureFilterFn<F>(F);
64
65impl<'g, F> FeatureFilterFn<F>
66where
67 F: FnMut(&FeatureGraph<'g>, FeatureId<'g>) -> bool,
68{
69 pub fn new(f: F) -> Self {
71 FeatureFilterFn(f)
72 }
73}
74
75impl<'g, F> FeatureFilter<'g> for FeatureFilterFn<F>
76where
77 F: FnMut(&FeatureGraph<'g>, FeatureId<'g>) -> bool,
78{
79 fn accept(&mut self, graph: &FeatureGraph<'g>, feature_id: FeatureId<'g>) -> bool {
80 (self.0)(graph, feature_id)
81 }
82}
83
84#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
89pub enum StandardFeatures {
90 None,
92
93 Default,
95
96 All,
98}
99
100impl StandardFeatures {
101 pub const VALUES: &'static [Self; 3] = &[
103 StandardFeatures::None,
104 StandardFeatures::Default,
105 StandardFeatures::All,
106 ];
107}
108
109impl<'g> FeatureFilter<'g> for StandardFeatures {
110 fn accept(&mut self, graph: &FeatureGraph<'g>, feature_id: FeatureId<'g>) -> bool {
111 match self {
112 StandardFeatures::None => {
113 feature_id.is_base()
115 }
116 StandardFeatures::Default => {
117 graph
120 .is_default_feature(feature_id)
121 .expect("feature IDs should be valid")
122 }
123 StandardFeatures::All => true,
124 }
125 }
126}
127
128pub fn named_feature_filter<'g: 'a, 'a>(
136 base: impl FeatureFilter<'g> + 'a,
137 features: impl IntoIterator<Item = &'a str>,
138) -> impl FeatureFilter<'g> + 'a {
139 let mut base = base;
140 let features: HashSet<_> = features.into_iter().collect();
141 FeatureFilterFn::new(move |feature_graph, feature_id| {
142 if base.accept(feature_graph, feature_id) {
143 return true;
144 }
145 match feature_id.label() {
146 FeatureLabel::Named(feature) => features.contains(feature),
147 _ => {
148 false
151 }
152 }
153 })
154}
155
156pub fn feature_id_filter<'g: 'a, 'a>(
161 base: impl FeatureFilter<'g> + 'a,
162 feature_ids: impl IntoIterator<Item = impl Into<FeatureId<'a>>>,
163) -> impl FeatureFilter<'g> + 'a {
164 let mut base = base;
165 let feature_ids: HashSet<_> = feature_ids
166 .into_iter()
167 .map(|feature_id| feature_id.into())
168 .collect();
169 FeatureFilterFn::new(move |feature_graph, feature_id| {
170 base.accept(feature_graph, feature_id) || feature_ids.contains(&feature_id)
171 })
172}
173
174#[derive(Clone, Debug)]
180pub struct FeatureQuery<'g> {
181 pub(super) graph: DebugIgnore<FeatureGraph<'g>>,
182 pub(in crate::graph) params: QueryParams<FeatureGraphSpec>,
183}
184
185assert_covariant!(FeatureQuery);
186
187impl<'g> FeatureGraph<'g> {
192 pub fn query_workspace(&self, filter: impl FeatureFilter<'g>) -> FeatureQuery<'g> {
197 self.package_graph
198 .query_workspace()
199 .to_feature_query(filter)
200 }
201
202 pub fn query_directed<'a>(
207 &self,
208 feature_ids: impl IntoIterator<Item = impl Into<FeatureId<'a>>>,
209 dep_direction: DependencyDirection,
210 ) -> Result<FeatureQuery<'g>, Error> {
211 match dep_direction {
212 DependencyDirection::Forward => self.query_forward(feature_ids),
213 DependencyDirection::Reverse => self.query_reverse(feature_ids),
214 }
215 }
216
217 pub fn query_forward<'a>(
221 &self,
222 feature_ids: impl IntoIterator<Item = impl Into<FeatureId<'a>>>,
223 ) -> Result<FeatureQuery<'g>, Error> {
224 let feature_ids = feature_ids.into_iter().map(|feature_id| feature_id.into());
225 Ok(FeatureQuery {
226 graph: DebugIgnore(*self),
227 params: QueryParams::Forward(self.feature_ixs(feature_ids)?),
228 })
229 }
230
231 pub fn query_reverse<'a>(
235 &self,
236 feature_ids: impl IntoIterator<Item = impl Into<FeatureId<'a>>>,
237 ) -> Result<FeatureQuery<'g>, Error> {
238 let feature_ids = feature_ids.into_iter().map(|feature_id| feature_id.into());
239 Ok(FeatureQuery {
240 graph: DebugIgnore(*self),
241 params: QueryParams::Reverse(self.feature_ixs(feature_ids)?),
242 })
243 }
244
245 pub(in crate::graph) fn query_from_parts(
246 &self,
247 feature_ixs: SortedSet<NodeIndex<FeatureIx>>,
248 direction: DependencyDirection,
249 ) -> FeatureQuery<'g> {
250 let params = match direction {
251 DependencyDirection::Forward => QueryParams::Forward(feature_ixs),
252 DependencyDirection::Reverse => QueryParams::Reverse(feature_ixs),
253 };
254 FeatureQuery {
255 graph: DebugIgnore(*self),
256 params,
257 }
258 }
259}
260
261impl<'g> FeatureQuery<'g> {
262 pub fn graph(&self) -> &FeatureGraph<'g> {
264 &self.graph
265 }
266
267 pub fn direction(&self) -> DependencyDirection {
269 self.params.direction()
270 }
271
272 pub fn initials<'a>(&'a self) -> impl ExactSizeIterator<Item = FeatureMetadata<'g>> + 'a {
276 let graph = self.graph;
277 self.params
278 .initials()
279 .iter()
280 .map(move |feature_ix| graph.metadata_for_ix(*feature_ix))
281 }
282
283 pub fn initial_packages<'a>(&'a self) -> impl Iterator<Item = PackageMetadata<'g>> + 'a {
287 self.initials().map(|feature| feature.package()).dedup()
289 }
290
291 pub fn starts_from_package(&self, package_id: &PackageId) -> Result<bool, Error> {
295 let package_ix = self.graph.package_graph.package_ix(package_id)?;
296 Ok(self.starts_from_package_ix(package_ix))
297 }
298
299 pub fn starts_from<'a>(&self, feature_id: impl Into<FeatureId<'a>>) -> Result<bool, Error> {
303 Ok(self
304 .params
305 .has_initial(self.graph.feature_ix(feature_id.into())?))
306 }
307
308 pub fn resolve(self) -> FeatureSet<'g> {
312 FeatureSet::new(self)
313 }
314
315 pub fn resolve_with(self, resolver: impl FeatureResolver<'g>) -> FeatureSet<'g> {
318 FeatureSet::with_resolver(self, resolver)
319 }
320
321 pub fn resolve_with_fn(
324 self,
325 resolver_fn: impl FnMut(&FeatureQuery<'g>, ConditionalLink<'g>) -> bool,
326 ) -> FeatureSet<'g> {
327 self.resolve_with(ResolverFn(resolver_fn))
328 }
329
330 pub(in crate::graph) fn starts_from_package_ix(
335 &self,
336 package_ix: NodeIndex<PackageIx>,
337 ) -> bool {
338 self.graph
339 .feature_ixs_for_package_ix(package_ix)
340 .any(|feature_ix| self.params.has_initial(feature_ix))
341 }
342}
343
344pub trait FeatureResolver<'g> {
347 fn accept(&mut self, query: &FeatureQuery<'g>, link: ConditionalLink<'g>) -> bool;
349}
350
351impl<'g, T> FeatureResolver<'g> for &mut T
352where
353 T: FeatureResolver<'g>,
354{
355 fn accept(&mut self, query: &FeatureQuery<'g>, link: ConditionalLink<'g>) -> bool {
356 (**self).accept(query, link)
357 }
358}
359
360impl<'g> FeatureResolver<'g> for Box<dyn FeatureResolver<'g> + '_> {
361 fn accept(&mut self, query: &FeatureQuery<'g>, link: ConditionalLink<'g>) -> bool {
362 (**self).accept(query, link)
363 }
364}
365
366impl<'g> FeatureResolver<'g> for &mut dyn FeatureResolver<'g> {
367 fn accept(&mut self, query: &FeatureQuery<'g>, link: ConditionalLink<'g>) -> bool {
368 (**self).accept(query, link)
369 }
370}
371
372#[derive(Clone, Debug)]
373struct ResolverFn<F>(pub F);
374
375impl<'g, F> FeatureResolver<'g> for ResolverFn<F>
376where
377 F: FnMut(&FeatureQuery<'g>, ConditionalLink<'g>) -> bool,
378{
379 fn accept(&mut self, query: &FeatureQuery<'g>, link: ConditionalLink<'g>) -> bool {
380 (self.0)(query, link)
381 }
382}