Skip to content

Commit d37eab6

Browse files
committed
Failing refactor.
1 parent 422b512 commit d37eab6

File tree

1 file changed

+131
-72
lines changed

1 file changed

+131
-72
lines changed

cli/npm/resolution/graph.rs

Lines changed: 131 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use std::borrow::Cow;
44
use std::collections::BTreeMap;
55
use std::collections::BTreeSet;
66
use std::collections::HashMap;
7-
use std::collections::HashSet;
87
use std::collections::VecDeque;
98
use std::sync::Arc;
109

@@ -33,49 +32,77 @@ use super::NpmResolutionPackage;
3332
use super::NpmVersionMatcher;
3433

3534
#[derive(Default, Clone)]
36-
struct VisitedVersions(HashSet<String>);
35+
struct VisitedVersionsPath {
36+
previous_node: Option<Arc<VisitedVersionsPath>>,
37+
visited_version: (String, NpmVersion),
38+
}
3739

38-
impl VisitedVersions {
39-
pub fn add(&mut self, id: &NpmPackageId) -> bool {
40-
self.0.insert(Self::id_as_key(id))
40+
impl VisitedVersionsPath {
41+
pub fn new(id: &NpmPackageId) -> Arc<Self> {
42+
Arc::new(Self {
43+
previous_node: None,
44+
visited_version: Self::id_as_name_and_version(id),
45+
})
4146
}
4247

43-
pub fn has_visited(&self, id: &NpmPackageId) -> bool {
44-
self.0.contains(&Self::id_as_key(id))
48+
pub fn with_id(
49+
self: &Arc<VisitedVersionsPath>,
50+
id: &NpmPackageId,
51+
) -> Option<Arc<Self>> {
52+
if self.has_visited(id) {
53+
None
54+
} else {
55+
Some(Arc::new(Self {
56+
previous_node: Some(self.clone()),
57+
visited_version: Self::id_as_name_and_version(id),
58+
}))
59+
}
60+
}
61+
62+
pub fn has_visited(self: &Arc<Self>, id: &NpmPackageId) -> bool {
63+
let mut maybe_next_node = Some(self);
64+
let name_and_version = Self::id_as_name_and_version(id);
65+
while let Some(next_node) = maybe_next_node {
66+
if next_node.visited_version == name_and_version {
67+
return true;
68+
}
69+
maybe_next_node = next_node.previous_node.as_ref();
70+
}
71+
false
4572
}
4673

47-
fn id_as_key(id: &NpmPackageId) -> String {
48-
// we only key on name and version in the id and not peer dependencies
49-
// because the peer dependencies could change above and below us,
50-
// but the names and versions won't
51-
format!("{}@{}", id.name, id.version)
74+
fn id_as_name_and_version(id: &NpmPackageId) -> (String, NpmVersion) {
75+
(id.name.clone(), id.version.clone())
5276
}
5377
}
5478

5579
#[derive(Default, Clone)]
5680
struct GraphPath {
57-
// todo(THIS PR): investigate if this should use a singly linked list too
58-
visited_versions: VisitedVersions,
59-
// todo(THIS PR): switch to a singly linked list here
60-
specifiers: Vec<String>,
81+
previous_node: Option<Arc<GraphPath>>,
82+
specifier: String,
6183
}
6284

6385
impl GraphPath {
64-
pub fn with_step(&self, specifier: &str, id: &NpmPackageId) -> GraphPath {
65-
let mut copy = self.clone();
66-
assert!(copy.visited_versions.add(id));
67-
copy.specifiers.push(specifier.to_string());
68-
copy
86+
pub fn new(specifier: String) -> Arc<Self> {
87+
Arc::new(Self {
88+
previous_node: None,
89+
specifier,
90+
})
91+
}
92+
93+
pub fn with_specifier(self: &Arc<Self>, specifier: String) -> Arc<Self> {
94+
Arc::new(Self {
95+
previous_node: Some(self.clone()),
96+
specifier,
97+
})
6998
}
7099

71-
pub fn with_specifier(&self, specifier: String) -> GraphPath {
72-
let mut copy = self.clone();
73-
copy.specifiers.push(specifier);
74-
copy
100+
pub fn pop(&self) -> Option<&Arc<Self>> {
101+
self.previous_node.as_ref()
75102
}
76103

77-
pub fn has_visited_version(&self, id: &NpmPackageId) -> bool {
78-
self.visited_versions.has_visited(id)
104+
pub fn is_last(&self) -> bool {
105+
self.previous_node.is_none()
79106
}
80107
}
81108

@@ -343,7 +370,8 @@ impl Graph {
343370
pub struct GraphDependencyResolver<'a, TNpmRegistryApi: NpmRegistryApi> {
344371
graph: &'a mut Graph,
345372
api: &'a TNpmRegistryApi,
346-
pending_unresolved_nodes: VecDeque<(VisitedVersions, Arc<Mutex<Node>>)>,
373+
pending_unresolved_nodes:
374+
VecDeque<(Arc<VisitedVersionsPath>, Arc<Mutex<Node>>)>,
347375
}
348376

349377
impl<'a, TNpmRegistryApi: NpmRegistryApi>
@@ -423,9 +451,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
423451
&node,
424452
&NodeParent::Req,
425453
);
426-
self
427-
.pending_unresolved_nodes
428-
.push_back((VisitedVersions::default(), node));
454+
self.add_pending_unresolved_node(None, &node);
429455
Ok(())
430456
}
431457

@@ -434,7 +460,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
434460
entry: &NpmDependencyEntry,
435461
package_info: NpmPackageInfo,
436462
parent_id: &NpmPackageId,
437-
visited_versions: &VisitedVersions,
463+
visited_versions: &Arc<VisitedVersionsPath>,
438464
) -> Result<(), AnyError> {
439465
let node = self.resolve_node_from_info(
440466
&entry.name,
@@ -456,10 +482,28 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
456482
&node,
457483
&NodeParent::Node(parent_id.clone()),
458484
);
485+
self.add_pending_unresolved_node(Some(visited_versions), &node);
486+
Ok(())
487+
}
488+
489+
fn add_pending_unresolved_node(
490+
&mut self,
491+
maybe_previous_visited_versions: Option<&Arc<VisitedVersionsPath>>,
492+
node: &Arc<Mutex<Node>>,
493+
) {
494+
let node_id = node.lock().id.clone();
495+
let visited_versions = match maybe_previous_visited_versions {
496+
Some(previous_visited_versions) => {
497+
match previous_visited_versions.with_id(&node_id) {
498+
Some(visited_versions) => visited_versions,
499+
None => return, // circular, don't visit this node
500+
}
501+
}
502+
None => VisitedVersionsPath::new(&node_id),
503+
};
459504
self
460505
.pending_unresolved_nodes
461-
.push_back((visited_versions.clone(), node));
462-
Ok(())
506+
.push_back((visited_versions, node.clone()));
463507
}
464508

465509
fn resolve_node_from_info(
@@ -503,18 +547,14 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
503547
pub async fn resolve_pending(&mut self) -> Result<(), AnyError> {
504548
while !self.pending_unresolved_nodes.is_empty() {
505549
// now go down through the dependencies by tree depth
506-
while let Some((mut visited_versions, parent_node)) =
550+
while let Some((visited_versions, parent_node)) =
507551
self.pending_unresolved_nodes.pop_front()
508552
{
509553
let (mut parent_id, deps) = {
510554
let parent_node = parent_node.lock();
511555
(parent_node.id.clone(), parent_node.deps.clone())
512556
};
513557

514-
if !visited_versions.add(&parent_id) {
515-
continue; // circular
516-
}
517-
518558
// cache all the dependencies' registry infos in parallel if should
519559
if !should_sync_download() {
520560
let handles = deps
@@ -578,7 +618,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
578618
parent_id: &NpmPackageId,
579619
peer_dep: &NpmDependencyEntry,
580620
peer_package_info: NpmPackageInfo,
581-
visited_ancestor_versions: &VisitedVersions,
621+
visited_ancestor_versions: &Arc<VisitedVersionsPath>,
582622
) -> Result<Option<NpmPackageId>, AnyError> {
583623
fn find_matching_child<'a>(
584624
peer_dep: &NpmDependencyEntry,
@@ -597,36 +637,59 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
597637
// Peer dependencies are resolved based on its ancestors' siblings.
598638
// If not found, then it resolves based on the version requirement if non-optional.
599639
let mut pending_ancestors = VecDeque::new(); // go up the tree by depth
600-
let path = GraphPath::default().with_step(specifier, parent_id);
640+
let path = GraphPath::new(specifier.to_string());
641+
let visited_versions = VisitedVersionsPath::new(parent_id);
601642

602643
// skip over the current node
603644
for (specifier, grand_parents) in
604645
self.graph.borrow_node(parent_id).parents.clone()
605646
{
606647
let path = path.with_specifier(specifier);
607648
for grand_parent in grand_parents {
608-
pending_ancestors.push_back((grand_parent, path.clone()));
649+
let visited_versions = match &grand_parent {
650+
NodeParent::Node(id) => visited_versions.with_id(id),
651+
NodeParent::Req => Some(visited_versions.clone()),
652+
};
653+
if let Some(visited_versions) = visited_versions {
654+
pending_ancestors.push_back((
655+
grand_parent,
656+
path.clone(),
657+
visited_versions,
658+
));
659+
}
609660
}
610661
}
611662

612-
while let Some((ancestor, path)) = pending_ancestors.pop_front() {
663+
while let Some((ancestor, path, visited_versions)) =
664+
pending_ancestors.pop_front()
665+
{
613666
match &ancestor {
614667
NodeParent::Node(ancestor_node_id) => {
615-
// we've gone in a full circle, so don't keep looking
616-
if path.has_visited_version(ancestor_node_id) {
617-
continue;
618-
}
619-
620668
let maybe_peer_dep_id = if ancestor_node_id.name == peer_dep.name
621669
&& peer_dep.version_req.satisfies(&ancestor_node_id.version)
622670
{
671+
// found it
623672
Some(ancestor_node_id.clone())
624673
} else {
674+
// continue searching up
625675
let ancestor = self.graph.borrow_node(ancestor_node_id);
626676
for (specifier, parents) in &ancestor.parents {
627-
let new_path = path.with_step(specifier, ancestor_node_id);
628677
for parent in parents {
629-
pending_ancestors.push_back((parent.clone(), new_path.clone()));
678+
let path = path.with_specifier(specifier.to_string());
679+
let visited_versions = match parent {
680+
NodeParent::Node(id) => visited_versions.with_id(id),
681+
NodeParent::Req => Some(visited_versions.clone()),
682+
};
683+
if let Some(visited_versions) = visited_versions {
684+
pending_ancestors.push_back((
685+
parent.clone(),
686+
path,
687+
visited_versions,
688+
));
689+
} else {
690+
// we've gone in a full circle, so don't keep looking
691+
continue;
692+
}
630693
}
631694
}
632695
find_matching_child(peer_dep, ancestor.children.values())
@@ -638,7 +701,7 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
638701
parents,
639702
ancestor_node_id,
640703
&peer_dep_id,
641-
path.specifiers,
704+
&path,
642705
visited_ancestor_versions,
643706
)));
644707
}
@@ -648,8 +711,8 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
648711
if let Some(child_id) =
649712
find_matching_child(peer_dep, self.graph.package_reqs.values())
650713
{
651-
let mut path = path.specifiers;
652-
let specifier = path.pop().unwrap(); // go back down one level
714+
let specifier = path.specifier.clone();
715+
let path = path.pop().unwrap(); // go back down one level from the package requirement
653716
let old_id =
654717
self.graph.package_reqs.get(&specifier).unwrap().clone();
655718
return Ok(Some(self.set_new_peer_dep(
@@ -684,8 +747,8 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
684747
previous_parents: BTreeMap<String, BTreeSet<NodeParent>>,
685748
node_id: &NpmPackageId,
686749
peer_dep_id: &NpmPackageId,
687-
mut path: Vec<String>,
688-
visited_ancestor_versions: &VisitedVersions,
750+
path: &GraphPath,
751+
visited_ancestor_versions: &Arc<VisitedVersionsPath>,
689752
) -> NpmPackageId {
690753
let mut peer_dep_id = Cow::Borrowed(peer_dep_id);
691754
let old_id = node_id;
@@ -708,15 +771,12 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
708771
}
709772

710773
// remove the previous parents from the old node
711-
let old_node_children = {
712-
for (specifier, parents) in &previous_parents {
713-
for parent in parents {
714-
self.graph.remove_child_parent(specifier, old_id, parent);
715-
}
774+
for (specifier, parents) in &previous_parents {
775+
for parent in parents {
776+
self.graph.remove_child_parent(specifier, old_id, parent);
716777
}
717-
let old_node = self.graph.borrow_node(old_id);
718-
old_node.children.clone()
719-
};
778+
}
779+
let old_node_children = self.graph.borrow_node(old_id).children.clone();
720780

721781
let (_, new_node) = self.graph.get_or_create_for_id(&new_id);
722782

@@ -743,26 +803,25 @@ impl<'a, TNpmRegistryApi: NpmRegistryApi>
743803
let mut bottom_parent_id = new_id.clone();
744804

745805
// continue going down the path
746-
if let Some(next_specifier) = path.pop() {
747-
if path.is_empty() {
806+
if let Some(path) = path.pop() {
807+
if path.is_last() {
748808
// this means we're at the peer dependency now
749809
debug!(
750810
"Resolved peer dependency for {} in {} to {}",
751-
&next_specifier, &new_id, &peer_dep_id,
811+
&path.specifier, &new_id, &peer_dep_id,
752812
);
753-
assert!(!old_node_children.contains_key(&next_specifier));
813+
assert!(!old_node_children.contains_key(&path.specifier));
754814
let node = self.graph.get_or_create_for_id(&peer_dep_id).1;
755815
self
756-
.pending_unresolved_nodes
757-
.push_back((visited_ancestor_versions.clone(), node.clone()));
816+
.add_pending_unresolved_node(Some(visited_ancestor_versions), &node);
758817
self
759818
.graph
760-
.set_child_parent_node(&next_specifier, &node, &new_id);
819+
.set_child_parent_node(&path.specifier, &node, &new_id);
761820
} else {
762-
let next_node_id = old_node_children.get(&next_specifier).unwrap();
821+
let next_node_id = old_node_children.get(&path.specifier).unwrap();
763822
bottom_parent_id = self.set_new_peer_dep(
764823
BTreeMap::from([(
765-
next_specifier.to_string(),
824+
path.specifier.to_string(),
766825
BTreeSet::from([NodeParent::Node(new_id.clone())]),
767826
)]),
768827
next_node_id,

0 commit comments

Comments
 (0)