1use crate::{
5 Error, PackageId,
6 graph::{
7 DependencyDirection, PackageGraph, PackageIx, PackageLink, PackageMetadata,
8 PackageResolver, PackageSet, ResolverFn,
9 feature::{FeatureFilter, FeatureQuery},
10 query_core::QueryParams,
11 },
12 sorted_set::SortedSet,
13};
14use camino::Utf8Path;
15use petgraph::prelude::*;
16
17#[derive(Clone, Debug)]
22pub struct PackageQuery<'g> {
23 pub(super) graph: &'g PackageGraph,
25 pub(super) params: QueryParams<PackageGraph>,
26}
27
28assert_covariant!(PackageQuery);
29
30impl PackageGraph {
35 pub fn query_workspace(&self) -> PackageQuery {
40 self.query_forward(self.workspace().member_ids())
41 .expect("workspace packages should all be known")
42 }
43
44 pub fn query_workspace_paths(
48 &self,
49 paths: impl IntoIterator<Item = impl AsRef<Utf8Path>>,
50 ) -> Result<PackageQuery, Error> {
51 let workspace = self.workspace();
52 let package_ixs = paths
53 .into_iter()
54 .map(|path| {
55 workspace
56 .member_by_path(path.as_ref())
57 .map(|package| package.package_ix())
58 })
59 .collect::<Result<SortedSet<_>, Error>>()?;
60
61 Ok(self.query_from_parts(package_ixs, DependencyDirection::Forward))
62 }
63
64 pub fn query_workspace_names(
70 &self,
71 names: impl IntoIterator<Item = impl AsRef<str>>,
72 ) -> Result<PackageQuery, Error> {
73 let workspace = self.workspace();
74 let package_ixs = names
75 .into_iter()
76 .map(|name| {
77 workspace
78 .member_by_name(name.as_ref())
79 .map(|package| package.package_ix())
80 })
81 .collect::<Result<SortedSet<_>, Error>>()?;
82
83 Ok(self.query_from_parts(package_ixs, DependencyDirection::Forward))
84 }
85
86 pub fn query_directed<'g, 'a>(
91 &'g self,
92 package_ids: impl IntoIterator<Item = &'a PackageId>,
93 dep_direction: DependencyDirection,
94 ) -> Result<PackageQuery<'g>, Error> {
95 match dep_direction {
96 DependencyDirection::Forward => self.query_forward(package_ids),
97 DependencyDirection::Reverse => self.query_reverse(package_ids),
98 }
99 }
100
101 pub fn query_forward<'g, 'a>(
105 &'g self,
106 package_ids: impl IntoIterator<Item = &'a PackageId>,
107 ) -> Result<PackageQuery<'g>, Error> {
108 Ok(PackageQuery {
109 graph: self,
110 params: QueryParams::Forward(self.package_ixs(package_ids)?),
111 })
112 }
113
114 pub fn query_reverse<'g, 'a>(
118 &'g self,
119 package_ids: impl IntoIterator<Item = &'a PackageId>,
120 ) -> Result<PackageQuery<'g>, Error> {
121 Ok(PackageQuery {
122 graph: self,
123 params: QueryParams::Reverse(self.package_ixs(package_ids)?),
124 })
125 }
126
127 pub(super) fn query_from_parts(
128 &self,
129 package_ixs: SortedSet<NodeIndex<PackageIx>>,
130 direction: DependencyDirection,
131 ) -> PackageQuery {
132 let params = match direction {
133 DependencyDirection::Forward => QueryParams::Forward(package_ixs),
134 DependencyDirection::Reverse => QueryParams::Reverse(package_ixs),
135 };
136 PackageQuery {
137 graph: self,
138 params,
139 }
140 }
141}
142
143impl<'g> PackageQuery<'g> {
144 pub fn graph(&self) -> &'g PackageGraph {
146 self.graph
147 }
148
149 pub fn direction(&self) -> DependencyDirection {
151 self.params.direction()
152 }
153
154 pub fn initials<'a>(&'a self) -> impl ExactSizeIterator<Item = PackageMetadata<'g>> + 'a {
158 let graph = self.graph;
159 self.params.initials().iter().map(move |package_ix| {
160 graph
161 .metadata(&graph.dep_graph[*package_ix])
162 .expect("valid ID")
163 })
164 }
165
166 pub fn starts_from(&self, package_id: &PackageId) -> Result<bool, Error> {
170 Ok(self.params.has_initial(self.graph.package_ix(package_id)?))
171 }
172
173 pub fn to_feature_query(&self, filter: impl FeatureFilter<'g>) -> FeatureQuery<'g> {
177 let package_ixs = self.params.initials();
178 let feature_graph = self.graph.feature_graph();
179 let feature_ixs =
180 feature_graph.feature_ixs_for_package_ixs_filtered(package_ixs.iter().copied(), filter);
181 feature_graph.query_from_parts(feature_ixs, self.direction())
182 }
183
184 pub fn resolve(self) -> PackageSet<'g> {
189 PackageSet::new(self)
190 }
191
192 pub fn resolve_with(self, resolver: impl PackageResolver<'g>) -> PackageSet<'g> {
195 PackageSet::with_resolver(self, resolver)
196 }
197
198 pub fn resolve_with_fn(
201 self,
202 resolver_fn: impl FnMut(&PackageQuery<'g>, PackageLink<'g>) -> bool,
203 ) -> PackageSet<'g> {
204 self.resolve_with(ResolverFn(resolver_fn))
205 }
206}