1use crate::{
5 Error, PackageId,
6 graph::{
7 DependencyDirection, PackageGraph, PackageIx, PackageLink, PackageLinkImpl,
8 PackageMetadata, PackageQuery,
9 feature::{FeatureFilter, FeatureSet},
10 resolve_core::{ResolveCore, Topo},
11 },
12 petgraph_support::{
13 IxBitSet,
14 dot::{DotFmt, DotVisitor, DotWrite},
15 edge_ref::GraphEdgeRef,
16 },
17};
18use camino::Utf8Path;
19use debug_ignore::DebugIgnore;
20use fixedbitset::FixedBitSet;
21use petgraph::{
22 prelude::*,
23 visit::{NodeFiltered, NodeRef},
24};
25use std::fmt;
26
27impl PackageGraph {
28 pub fn resolve_all(&self) -> PackageSet<'_> {
36 PackageSet {
37 graph: DebugIgnore(self),
38 core: ResolveCore::all_nodes(&self.dep_graph),
39 }
40 }
41
42 pub fn resolve_none(&self) -> PackageSet<'_> {
44 PackageSet {
45 graph: DebugIgnore(self),
46 core: ResolveCore::empty(&self.dep_graph),
47 }
48 }
49
50 pub fn resolve_ids<'a>(
56 &self,
57 package_ids: impl IntoIterator<Item = &'a PackageId>,
58 ) -> Result<PackageSet<'_>, Error> {
59 let included: IxBitSet = self.package_ixs(package_ids)?;
60 Ok(PackageSet::from_included(self, included))
61 }
62
63 pub fn resolve_workspace(&self) -> PackageSet<'_> {
67 let included: IxBitSet = self
68 .workspace()
69 .iter_by_path()
70 .map(|(_, package)| package.package_ix())
71 .collect();
72 PackageSet::from_included(self, included)
73 }
74
75 pub fn resolve_workspace_paths(
81 &self,
82 paths: impl IntoIterator<Item = impl AsRef<Utf8Path>>,
83 ) -> Result<PackageSet<'_>, Error> {
84 let workspace = self.workspace();
85 let included: IxBitSet = paths
86 .into_iter()
87 .map(|path| {
88 workspace
89 .member_by_path(path.as_ref())
90 .map(|package| package.package_ix())
91 })
92 .collect::<Result<_, Error>>()?;
93 Ok(PackageSet::from_included(self, included))
94 }
95
96 pub fn resolve_workspace_names(
102 &self,
103 names: impl IntoIterator<Item = impl AsRef<str>>,
104 ) -> Result<PackageSet<'_>, Error> {
105 let workspace = self.workspace();
106 let included: IxBitSet = names
107 .into_iter()
108 .map(|name| {
109 workspace
110 .member_by_name(name.as_ref())
111 .map(|package| package.package_ix())
112 })
113 .collect::<Result<_, _>>()?;
114 Ok(PackageSet::from_included(self, included))
115 }
116
117 pub fn resolve_package_name(&self, name: impl AsRef<str>) -> PackageSet<'_> {
121 let name = name.as_ref();
127 let included: IxBitSet = self
128 .packages()
129 .filter_map(|package| {
130 if package.name() == name {
131 Some(package.package_ix())
132 } else {
133 None
134 }
135 })
136 .collect();
137 PackageSet::from_included(self, included)
138 }
139}
140
141#[derive(Clone, Debug)]
146pub struct PackageSet<'g> {
147 graph: DebugIgnore<&'g PackageGraph>,
148 core: ResolveCore<PackageGraph>,
149}
150
151assert_covariant!(PackageSet);
152
153impl<'g> PackageSet<'g> {
154 pub(super) fn new(query: PackageQuery<'g>) -> Self {
155 let graph = query.graph();
156 Self {
157 graph: DebugIgnore(graph),
158 core: ResolveCore::new(
159 graph.dep_graph(),
160 query.initials().sorted_ixs(),
161 query.direction,
162 ),
163 }
164 }
165
166 pub(super) fn from_ixs(
167 graph: &'g PackageGraph,
168 package_ixs: impl IntoIterator<Item = NodeIndex<PackageIx>>,
169 ) -> Self {
170 Self {
171 graph: DebugIgnore(graph),
172 core: ResolveCore::from_ixs(package_ixs, graph.dep_graph()),
173 }
174 }
175
176 pub(super) fn from_included(graph: &'g PackageGraph, included: impl Into<FixedBitSet>) -> Self {
177 Self {
178 graph: DebugIgnore(graph),
179 core: ResolveCore::from_included(included, graph.dep_graph()),
180 }
181 }
182
183 pub(super) fn with_link_visitor(
184 query: PackageQuery<'g>,
185 mut visitor: impl PackageLinkVisitor<'g>,
186 ) -> Self {
187 let graph = query.graph();
188 let cx = PackageLinkContext { query };
189 Self {
190 graph: DebugIgnore(graph),
191 core: ResolveCore::with_edge_filter(
192 graph.dep_graph(),
193 cx.query().initials().sorted_ixs(),
194 cx.direction(),
195 |edge| {
196 let link = graph.edge_ref_to_link(edge);
197 visitor.visit_link(&cx, link)
198 },
199 ),
200 }
201 }
202
203 pub fn len(&self) -> usize {
205 self.core.len()
206 }
207
208 pub fn is_empty(&self) -> bool {
210 self.core.is_empty()
211 }
212
213 pub fn contains(&self, package_id: &PackageId) -> Result<bool, Error> {
217 Ok(self.contains_ix(self.graph.package_ix(package_id)?))
218 }
219
220 pub fn to_package_query(&self, direction: DependencyDirection) -> PackageQuery<'g> {
224 PackageQuery {
225 initials: self.clone(),
226 direction,
227 }
228 }
229
230 pub fn union(&self, other: &Self) -> Self {
241 assert!(
242 ::std::ptr::eq(self.graph.0, other.graph.0),
243 "package graphs passed into union() match"
244 );
245 let mut res = self.clone();
246 res.core.union_with(&other.core);
247 res
248 }
249
250 pub fn intersection(&self, other: &Self) -> Self {
256 assert!(
257 ::std::ptr::eq(self.graph.0, other.graph.0),
258 "package graphs passed into intersection() match"
259 );
260 let mut res = self.clone();
261 res.core.intersect_with(&other.core);
262 res
263 }
264
265 pub fn difference(&self, other: &Self) -> Self {
271 assert!(
272 ::std::ptr::eq(self.graph.0, other.graph.0),
273 "package graphs passed into difference() match"
274 );
275 Self {
276 graph: self.graph,
277 core: self.core.difference(&other.core),
278 }
279 }
280
281 pub fn symmetric_difference(&self, other: &Self) -> Self {
288 assert!(
289 ::std::ptr::eq(self.graph.0, other.graph.0),
290 "package graphs passed into symmetric_difference() match"
291 );
292 let mut res = self.clone();
293 res.core.symmetric_difference_with(&other.core);
294 res
295 }
296
297 pub fn filter(
307 &self,
308 direction: DependencyDirection,
309 mut callback: impl FnMut(PackageMetadata<'g>) -> bool,
310 ) -> Self {
311 let graph = *self.graph;
312 let included: IxBitSet = self
313 .packages(direction)
314 .filter_map(move |package| {
315 let package_ix = package.package_ix();
316 if callback(package) {
317 Some(package_ix)
318 } else {
319 None
320 }
321 })
322 .collect();
323 Self::from_included(graph, included)
324 }
325
326 pub fn partition(
337 &self,
338 direction: DependencyDirection,
339 mut callback: impl FnMut(PackageMetadata<'g>) -> bool,
340 ) -> (Self, Self) {
341 let graph = *self.graph;
342 let mut left = IxBitSet::with_capacity(graph.dep_graph().node_count());
343 let mut right = left.clone();
344
345 self.packages(direction).for_each(|package| {
346 let package_ix = package.package_ix();
347 match callback(package) {
348 true => left.insert_node_ix(package_ix),
349 false => right.insert_node_ix(package_ix),
350 }
351 });
352 (
353 Self::from_included(graph, left),
354 Self::from_included(graph, right),
355 )
356 }
357
358 pub fn filter_partition(
370 &self,
371 direction: DependencyDirection,
372 mut callback: impl FnMut(PackageMetadata<'g>) -> Option<bool>,
373 ) -> (Self, Self) {
374 let graph = *self.graph;
375 let mut left = IxBitSet::with_capacity(graph.dep_graph().node_count());
376 let mut right = left.clone();
377
378 self.packages(direction).for_each(|package| {
379 let package_ix = package.package_ix();
380 match callback(package) {
381 Some(true) => left.insert_node_ix(package_ix),
382 Some(false) => right.insert_node_ix(package_ix),
383 None => {}
384 }
385 });
386 (
387 Self::from_included(graph, left),
388 Self::from_included(graph, right),
389 )
390 }
391
392 pub fn to_feature_set(&self, filter: impl FeatureFilter<'g>) -> FeatureSet<'g> {
401 let feature_graph = self.graph.feature_graph();
402 let included: IxBitSet = feature_graph.feature_ixs_for_package_ixs_filtered(
403 self.ixs(DependencyDirection::Forward),
405 filter,
406 );
407 FeatureSet::from_included(feature_graph, included)
408 }
409
410 pub fn package_ids<'a>(
422 &'a self,
423 direction: DependencyDirection,
424 ) -> impl ExactSizeIterator<Item = &'g PackageId> + 'a {
425 let graph = self.graph;
426 self.core
427 .topo(self.graph.sccs(), direction)
428 .map(move |package_ix| &graph.dep_graph[package_ix])
429 }
430
431 pub(super) fn ixs(&'g self, direction: DependencyDirection) -> Topo<'g, PackageGraph> {
432 self.core.topo(self.graph.sccs(), direction)
433 }
434
435 pub fn packages<'a>(
443 &'a self,
444 direction: DependencyDirection,
445 ) -> impl ExactSizeIterator<Item = PackageMetadata<'g>> + 'a {
446 let graph = self.graph;
447 self.package_ids(direction)
448 .map(move |package_id| graph.metadata(package_id).expect("known package IDs"))
449 }
450
451 pub fn root_ids<'a>(
463 &'a self,
464 direction: DependencyDirection,
465 ) -> impl ExactSizeIterator<Item = &'g PackageId> + 'a {
466 let dep_graph = &self.graph.dep_graph;
467 self.core
468 .roots(self.graph.dep_graph(), self.graph.sccs(), direction)
469 .into_iter()
470 .map(move |package_ix| &dep_graph[package_ix])
471 }
472
473 pub fn root_packages<'a>(
485 &'a self,
486 direction: DependencyDirection,
487 ) -> impl ExactSizeIterator<Item = PackageMetadata<'g>> + 'a {
488 let package_graph = self.graph;
489 self.core
490 .roots(self.graph.dep_graph(), self.graph.sccs(), direction)
491 .into_iter()
492 .map(move |package_ix| {
493 package_graph
494 .metadata(&package_graph.dep_graph[package_ix])
495 .expect("invalid node index")
496 })
497 }
498
499 pub fn links<'a>(
515 &'a self,
516 direction: DependencyDirection,
517 ) -> impl Iterator<Item = PackageLink<'g>> + 'a {
518 let graph = self.graph.0;
519 self.core
520 .links(graph.dep_graph(), graph.sccs(), direction)
521 .map(move |(source_ix, target_ix, edge_ix)| {
522 PackageLink::new(graph, source_ix, target_ix, edge_ix, None)
523 })
524 }
525
526 pub fn display_dot<'a, V: PackageDotVisitor + 'g>(
528 &'a self,
529 visitor: V,
530 ) -> impl fmt::Display + 'a {
531 let node_filtered = NodeFiltered(self.graph.dep_graph(), &self.core.included);
532 DotFmt::new(node_filtered, VisitorWrap::new(self.graph.0, visitor))
533 }
534
535 pub(super) fn graph(&self) -> &'g PackageGraph {
540 self.graph.0
541 }
542
543 pub(super) fn sorted_ixs(&self) -> impl Iterator<Item = NodeIndex<PackageIx>> + '_ {
545 self.core.included.ones()
546 }
547
548 pub(super) fn contains_ix(&self, package_ix: NodeIndex<PackageIx>) -> bool {
549 self.core.contains(package_ix)
550 }
551}
552
553impl PartialEq for PackageSet<'_> {
554 fn eq(&self, other: &Self) -> bool {
555 ::std::ptr::eq(self.graph.0, other.graph.0) && self.core == other.core
556 }
557}
558
559impl Eq for PackageSet<'_> {}
560
561#[derive(Clone, Debug)]
564pub struct PackageLinkContext<'g> {
565 query: PackageQuery<'g>,
566}
567
568impl<'g> PackageLinkContext<'g> {
569 pub fn query(&self) -> &PackageQuery<'g> {
571 &self.query
572 }
573
574 pub fn direction(&self) -> DependencyDirection {
576 self.query.direction()
577 }
578
579 pub fn starts_from_initial(&self, link: &PackageLink<'g>) -> bool {
582 let package_ix = match self.direction() {
583 DependencyDirection::Forward => link.from().package_ix(),
584 DependencyDirection::Reverse => link.to().package_ix(),
585 };
586 self.query.initials().contains_ix(package_ix)
587 }
588}
589
590pub trait PackageLinkVisitor<'g> {
593 fn visit_link(&mut self, cx: &PackageLinkContext<'g>, link: PackageLink<'g>) -> bool;
598}
599
600impl<'g, T> PackageLinkVisitor<'g> for &mut T
601where
602 T: PackageLinkVisitor<'g>,
603{
604 fn visit_link(&mut self, cx: &PackageLinkContext<'g>, link: PackageLink<'g>) -> bool {
605 (**self).visit_link(cx, link)
606 }
607}
608
609impl<'g> PackageLinkVisitor<'g> for Box<dyn PackageLinkVisitor<'g> + '_> {
610 fn visit_link(&mut self, cx: &PackageLinkContext<'g>, link: PackageLink<'g>) -> bool {
611 (**self).visit_link(cx, link)
612 }
613}
614
615impl<'g> PackageLinkVisitor<'g> for &mut dyn PackageLinkVisitor<'g> {
616 fn visit_link(&mut self, cx: &PackageLinkContext<'g>, link: PackageLink<'g>) -> bool {
617 (**self).visit_link(cx, link)
618 }
619}
620
621pub(super) struct LinkVisitorFn<F>(pub(super) F);
622
623impl<'g, F> PackageLinkVisitor<'g> for LinkVisitorFn<F>
624where
625 F: FnMut(&PackageLinkContext<'g>, PackageLink<'g>) -> bool,
626{
627 fn visit_link(&mut self, cx: &PackageLinkContext<'g>, link: PackageLink<'g>) -> bool {
628 (self.0)(cx, link)
629 }
630}
631
632pub trait PackageDotVisitor {
634 fn visit_package(&self, package: PackageMetadata<'_>, f: &mut DotWrite<'_, '_>) -> fmt::Result;
637
638 fn visit_link(&self, link: PackageLink<'_>, f: &mut DotWrite<'_, '_>) -> fmt::Result;
641}
642
643struct VisitorWrap<'g, V> {
644 graph: &'g PackageGraph,
645 inner: V,
646}
647
648impl<'g, V> VisitorWrap<'g, V> {
649 fn new(graph: &'g PackageGraph, inner: V) -> Self {
650 Self { graph, inner }
651 }
652}
653
654impl<'g, V, NR, ER> DotVisitor<NR, ER> for VisitorWrap<'g, V>
655where
656 V: PackageDotVisitor,
657 NR: NodeRef<NodeId = NodeIndex<PackageIx>, Weight = PackageId>,
658 ER: GraphEdgeRef<'g, PackageLinkImpl, PackageIx>,
659{
660 fn visit_node(&self, node: NR, f: &mut DotWrite<'_, '_>) -> fmt::Result {
661 let metadata = self
662 .graph
663 .metadata(node.weight())
664 .expect("visited node should have associated metadata");
665 self.inner.visit_package(metadata, f)
666 }
667
668 fn visit_edge(&self, edge: ER, f: &mut DotWrite<'_, '_>) -> fmt::Result {
669 let link = self.graph.edge_ref_to_link(edge.into_edge_reference());
670 self.inner.visit_link(link, f)
671 }
672}