1use crate::{
5 debug_ignore::DebugIgnore,
6 graph::{
7 feature::{FeatureFilter, FeatureSet},
8 resolve_core::{ResolveCore, Topo},
9 DependencyDirection, PackageGraph, PackageIx, PackageLink, PackageLinkImpl,
10 PackageMetadata, PackageQuery,
11 },
12 petgraph_support::{
13 dot::{DotFmt, DotVisitor, DotWrite},
14 edge_ref::GraphEdgeRef,
15 IxBitSet,
16 },
17 sorted_set::SortedSet,
18 Error, PackageId,
19};
20use camino::Utf8Path;
21use fixedbitset::FixedBitSet;
22use petgraph::{
23 prelude::*,
24 visit::{NodeFiltered, NodeRef},
25};
26use std::fmt;
27
28impl PackageGraph {
29 pub fn resolve_all(&self) -> PackageSet {
37 PackageSet {
38 graph: DebugIgnore(self),
39 core: ResolveCore::all_nodes(&self.dep_graph),
40 }
41 }
42
43 pub fn resolve_none(&self) -> PackageSet {
45 PackageSet {
46 graph: DebugIgnore(self),
47 core: ResolveCore::empty(),
48 }
49 }
50
51 pub fn resolve_ids<'a>(
57 &self,
58 package_ids: impl IntoIterator<Item = &'a PackageId>,
59 ) -> Result<PackageSet, Error> {
60 Ok(PackageSet {
61 graph: DebugIgnore(self),
62 core: ResolveCore::from_included::<IxBitSet>(self.package_ixs(package_ids)?),
63 })
64 }
65
66 pub fn resolve_workspace(&self) -> PackageSet {
70 let included: IxBitSet = self
71 .workspace()
72 .iter_by_path()
73 .map(|(_, package)| package.package_ix())
74 .collect();
75 PackageSet {
76 graph: DebugIgnore(self),
77 core: ResolveCore::from_included(included),
78 }
79 }
80
81 pub fn resolve_workspace_paths(
87 &self,
88 paths: impl IntoIterator<Item = impl AsRef<Utf8Path>>,
89 ) -> Result<PackageSet, Error> {
90 let workspace = self.workspace();
91 let included: IxBitSet = paths
92 .into_iter()
93 .map(|path| {
94 workspace
95 .member_by_path(path.as_ref())
96 .map(|package| package.package_ix())
97 })
98 .collect::<Result<_, Error>>()?;
99 Ok(PackageSet {
100 graph: DebugIgnore(self),
101 core: ResolveCore::from_included(included),
102 })
103 }
104
105 pub fn resolve_workspace_names(
111 &self,
112 names: impl IntoIterator<Item = impl AsRef<str>>,
113 ) -> Result<PackageSet, Error> {
114 let workspace = self.workspace();
115 let included: IxBitSet = names
116 .into_iter()
117 .map(|name| {
118 workspace
119 .member_by_name(name.as_ref())
120 .map(|package| package.package_ix())
121 })
122 .collect::<Result<_, _>>()?;
123 Ok(PackageSet {
124 graph: DebugIgnore(self),
125 core: ResolveCore::from_included(included),
126 })
127 }
128
129 pub fn resolve_package_name(&self, name: impl AsRef<str>) -> PackageSet {
133 let name = name.as_ref();
139 let included: IxBitSet = self
140 .packages()
141 .filter_map(|package| {
142 if package.name() == name {
143 Some(package.package_ix())
144 } else {
145 None
146 }
147 })
148 .collect();
149 PackageSet::from_included(self, included)
150 }
151}
152
153#[derive(Clone, Debug)]
157pub struct PackageSet<'g> {
158 graph: DebugIgnore<&'g PackageGraph>,
159 core: ResolveCore<PackageGraph>,
160}
161
162assert_covariant!(PackageSet);
163
164impl<'g> PackageSet<'g> {
165 pub(super) fn new(query: PackageQuery<'g>) -> Self {
166 let graph = query.graph;
167 Self {
168 graph: DebugIgnore(graph),
169 core: ResolveCore::new(graph.dep_graph(), query.params),
170 }
171 }
172
173 pub(super) fn from_included(graph: &'g PackageGraph, included: impl Into<FixedBitSet>) -> Self {
174 Self {
175 graph: DebugIgnore(graph),
176 core: ResolveCore::from_included(included),
177 }
178 }
179
180 pub(super) fn with_resolver(
181 query: PackageQuery<'g>,
182 mut resolver: impl PackageResolver<'g>,
183 ) -> Self {
184 let graph = query.graph;
185 let params = query.params.clone();
186 Self {
187 graph: DebugIgnore(graph),
188 core: ResolveCore::with_edge_filter(graph.dep_graph(), params, |edge| {
189 let link = graph.edge_ref_to_link(edge);
190 resolver.accept(&query, link)
191 }),
192 }
193 }
194
195 pub fn len(&self) -> usize {
197 self.core.len()
198 }
199
200 pub fn is_empty(&self) -> bool {
202 self.core.is_empty()
203 }
204
205 pub fn contains(&self, package_id: &PackageId) -> Result<bool, Error> {
209 Ok(self.contains_ix(self.graph.package_ix(package_id)?))
210 }
211
212 pub fn to_package_query(&self, direction: DependencyDirection) -> PackageQuery<'g> {
216 let package_ixs = SortedSet::new(
217 self.core
218 .included
219 .ones()
220 .map(NodeIndex::new)
221 .collect::<Vec<_>>(),
222 );
223 self.graph.query_from_parts(package_ixs, direction)
224 }
225
226 pub fn union(&self, other: &Self) -> Self {
237 assert!(
238 ::std::ptr::eq(self.graph.0, other.graph.0),
239 "package graphs passed into union() match"
240 );
241 let mut res = self.clone();
242 res.core.union_with(&other.core);
243 res
244 }
245
246 pub fn intersection(&self, other: &Self) -> Self {
252 assert!(
253 ::std::ptr::eq(self.graph.0, other.graph.0),
254 "package graphs passed into intersection() match"
255 );
256 let mut res = self.clone();
257 res.core.intersect_with(&other.core);
258 res
259 }
260
261 pub fn difference(&self, other: &Self) -> Self {
267 assert!(
268 ::std::ptr::eq(self.graph.0, other.graph.0),
269 "package graphs passed into difference() match"
270 );
271 Self {
272 graph: self.graph,
273 core: self.core.difference(&other.core),
274 }
275 }
276
277 pub fn symmetric_difference(&self, other: &Self) -> Self {
284 assert!(
285 ::std::ptr::eq(self.graph.0, other.graph.0),
286 "package graphs passed into symmetric_difference() match"
287 );
288 let mut res = self.clone();
289 res.core.symmetric_difference_with(&other.core);
290 res
291 }
292
293 pub fn filter(
303 &self,
304 direction: DependencyDirection,
305 mut callback: impl FnMut(PackageMetadata<'g>) -> bool,
306 ) -> Self {
307 let graph = *self.graph;
308 let included: IxBitSet = self
309 .packages(direction)
310 .filter_map(move |package| {
311 let package_ix = package.package_ix();
312 if callback(package) {
313 Some(package_ix)
314 } else {
315 None
316 }
317 })
318 .collect();
319 Self::from_included(graph, included)
320 }
321
322 pub fn partition(
333 &self,
334 direction: DependencyDirection,
335 mut callback: impl FnMut(PackageMetadata<'g>) -> bool,
336 ) -> (Self, Self) {
337 let graph = *self.graph;
338 let mut left = IxBitSet::with_capacity(self.core.included.len());
339 let mut right = left.clone();
340
341 self.packages(direction).for_each(|package| {
342 let package_ix = package.package_ix();
343 match callback(package) {
344 true => left.insert_node_ix(package_ix),
345 false => right.insert_node_ix(package_ix),
346 }
347 });
348 (
349 Self::from_included(graph, left),
350 Self::from_included(graph, right),
351 )
352 }
353
354 pub fn filter_partition(
366 &self,
367 direction: DependencyDirection,
368 mut callback: impl FnMut(PackageMetadata<'g>) -> Option<bool>,
369 ) -> (Self, Self) {
370 let graph = *self.graph;
371 let mut left = IxBitSet::with_capacity(self.core.included.len());
372 let mut right = left.clone();
373
374 self.packages(direction).for_each(|package| {
375 let package_ix = package.package_ix();
376 match callback(package) {
377 Some(true) => left.insert_node_ix(package_ix),
378 Some(false) => right.insert_node_ix(package_ix),
379 None => {}
380 }
381 });
382 (
383 Self::from_included(graph, left),
384 Self::from_included(graph, right),
385 )
386 }
387
388 pub fn to_feature_set(&self, filter: impl FeatureFilter<'g>) -> FeatureSet<'g> {
397 let feature_graph = self.graph.feature_graph();
398 let included: IxBitSet = feature_graph.feature_ixs_for_package_ixs_filtered(
399 self.ixs(DependencyDirection::Forward),
401 filter,
402 );
403 FeatureSet::from_included(feature_graph, included)
404 }
405
406 pub fn package_ids<'a>(
418 &'a self,
419 direction: DependencyDirection,
420 ) -> impl ExactSizeIterator<Item = &'g PackageId> + 'a {
421 let graph = self.graph;
422 self.core
423 .topo(self.graph.sccs(), direction)
424 .map(move |package_ix| &graph.dep_graph[package_ix])
425 }
426
427 pub(super) fn ixs(&'g self, direction: DependencyDirection) -> Topo<'g, PackageGraph> {
428 self.core.topo(self.graph.sccs(), direction)
429 }
430
431 pub fn packages<'a>(
439 &'a self,
440 direction: DependencyDirection,
441 ) -> impl ExactSizeIterator<Item = PackageMetadata<'g>> + 'a {
442 let graph = self.graph;
443 self.package_ids(direction)
444 .map(move |package_id| graph.metadata(package_id).expect("known package IDs"))
445 }
446
447 pub fn root_ids<'a>(
459 &'a self,
460 direction: DependencyDirection,
461 ) -> impl ExactSizeIterator<Item = &'g PackageId> + 'a {
462 let dep_graph = &self.graph.dep_graph;
463 self.core
464 .roots(self.graph.dep_graph(), self.graph.sccs(), direction)
465 .into_iter()
466 .map(move |package_ix| &dep_graph[package_ix])
467 }
468
469 pub fn root_packages<'a>(
481 &'a self,
482 direction: DependencyDirection,
483 ) -> impl ExactSizeIterator<Item = PackageMetadata<'g>> + 'a {
484 let package_graph = self.graph;
485 self.core
486 .roots(self.graph.dep_graph(), self.graph.sccs(), direction)
487 .into_iter()
488 .map(move |package_ix| {
489 package_graph
490 .metadata(&package_graph.dep_graph[package_ix])
491 .expect("invalid node index")
492 })
493 }
494
495 pub fn links<'a>(
511 &'a self,
512 direction: DependencyDirection,
513 ) -> impl Iterator<Item = PackageLink<'g>> + 'a {
514 let graph = self.graph.0;
515 self.core
516 .links(graph.dep_graph(), graph.sccs(), direction)
517 .map(move |(source_ix, target_ix, edge_ix)| {
518 PackageLink::new(graph, source_ix, target_ix, edge_ix, None)
519 })
520 }
521
522 pub fn display_dot<'a, V: PackageDotVisitor + 'g>(
524 &'a self,
525 visitor: V,
526 ) -> impl fmt::Display + 'a {
527 let node_filtered = NodeFiltered(self.graph.dep_graph(), &self.core.included);
528 DotFmt::new(node_filtered, VisitorWrap::new(self.graph.0, visitor))
529 }
530
531 #[allow(dead_code)]
537 pub(super) fn ixs_unordered(&self) -> impl Iterator<Item = NodeIndex<PackageIx>> + '_ {
538 self.core.included.ones().map(NodeIndex::new)
539 }
540
541 pub(super) fn contains_ix(&self, package_ix: NodeIndex<PackageIx>) -> bool {
542 self.core.contains(package_ix)
543 }
544}
545
546impl PartialEq for PackageSet<'_> {
547 fn eq(&self, other: &Self) -> bool {
548 ::std::ptr::eq(self.graph.0, other.graph.0) && self.core == other.core
549 }
550}
551
552impl Eq for PackageSet<'_> {}
553
554pub trait PackageResolver<'g> {
557 fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool;
562}
563
564impl<'g, T> PackageResolver<'g> for &mut T
565where
566 T: PackageResolver<'g>,
567{
568 fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool {
569 (**self).accept(query, link)
570 }
571}
572
573impl<'g> PackageResolver<'g> for Box<dyn PackageResolver<'g> + '_> {
574 fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool {
575 (**self).accept(query, link)
576 }
577}
578
579impl<'g> PackageResolver<'g> for &mut dyn PackageResolver<'g> {
580 fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool {
581 (**self).accept(query, link)
582 }
583}
584
585pub(super) struct ResolverFn<F>(pub(super) F);
586
587impl<'g, F> PackageResolver<'g> for ResolverFn<F>
588where
589 F: FnMut(&PackageQuery<'g>, PackageLink<'g>) -> bool,
590{
591 fn accept(&mut self, query: &PackageQuery<'g>, link: PackageLink<'g>) -> bool {
592 (self.0)(query, link)
593 }
594}
595
596pub trait PackageDotVisitor {
598 fn visit_package(&self, package: PackageMetadata<'_>, f: &mut DotWrite<'_, '_>) -> fmt::Result;
601
602 fn visit_link(&self, link: PackageLink<'_>, f: &mut DotWrite<'_, '_>) -> fmt::Result;
605}
606
607struct VisitorWrap<'g, V> {
608 graph: &'g PackageGraph,
609 inner: V,
610}
611
612impl<'g, V> VisitorWrap<'g, V> {
613 fn new(graph: &'g PackageGraph, inner: V) -> Self {
614 Self { graph, inner }
615 }
616}
617
618impl<'g, V, NR, ER> DotVisitor<NR, ER> for VisitorWrap<'g, V>
619where
620 V: PackageDotVisitor,
621 NR: NodeRef<NodeId = NodeIndex<PackageIx>, Weight = PackageId>,
622 ER: GraphEdgeRef<'g, PackageLinkImpl, PackageIx>,
623{
624 fn visit_node(&self, node: NR, f: &mut DotWrite<'_, '_>) -> fmt::Result {
625 let metadata = self
626 .graph
627 .metadata(node.weight())
628 .expect("visited node should have associated metadata");
629 self.inner.visit_package(metadata, f)
630 }
631
632 fn visit_edge(&self, edge: ER, f: &mut DotWrite<'_, '_>) -> fmt::Result {
633 let link = self.graph.edge_ref_to_link(edge.into_edge_reference());
634 self.inner.visit_link(link, f)
635 }
636}