Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add method to get subset of resolution snapshot #73

Merged
merged 6 commits into from
Nov 29, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions src/resolution/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,55 @@ impl NpmResolutionSnapshot {
}
}

pub fn subset(&self, package_reqs: &[PackageReq]) -> Option<Self> {
nathanwhit marked this conversation as resolved.
Show resolved Hide resolved
nathanwhit marked this conversation as resolved.
Show resolved Hide resolved
if !package_reqs
.iter()
.all(|req| self.package_reqs.contains_key(req))
nathanwhit marked this conversation as resolved.
Show resolved Hide resolved
{
return None;
}

let mut new_package_reqs = HashMap::with_capacity(package_reqs.len());
let mut packages = HashMap::with_capacity(package_reqs.len() * 2);
let mut packages_by_name: HashMap<String, Vec<NpmPackageId>> =
HashMap::with_capacity(package_reqs.len());
let mut root_packages = HashMap::with_capacity(package_reqs.len());

let mut visited = HashSet::with_capacity(packages.len());

let mut stack = Vec::new();
for req in package_reqs {
let nv = self.package_reqs.get(req)?.clone();
let id = self.root_packages.get(&nv)?;
nathanwhit marked this conversation as resolved.
Show resolved Hide resolved
new_package_reqs.insert(req.clone(), nv.clone());
root_packages.insert(nv, id.clone());
visited.insert(id);
stack.push(id);
}

while let Some(id) = stack.pop() {
let package = self.package_from_id(id)?;
packages_by_name
.entry(package.id.nv.name.to_string())
.or_default()
.push(package.id.clone());
let package = self.package_from_id(id)?;
packages.insert(id.clone(), package.clone());
for dep in package.dependencies.values() {
if visited.insert(dep) {
stack.push(dep);
}
}
}

Some(Self {
package_reqs: new_package_reqs,
packages,
packages_by_name,
root_packages,
})
}

/// Gets the snapshot as a valid serialized snapshot.
pub fn as_valid_serialized(&self) -> ValidSerializedNpmResolutionSnapshot {
ValidSerializedNpmResolutionSnapshot(SerializedNpmResolutionSnapshot {
Expand Down Expand Up @@ -1422,4 +1471,68 @@ mod tests {
])
);
}

fn package(
id: &str,
dependencies: &[(&str, &str)],
) -> SerializedNpmResolutionSnapshotPackage {
SerializedNpmResolutionSnapshotPackage {
id: NpmPackageId::from_serialized(id).unwrap(),
dependencies: deps(dependencies),
system: Default::default(),
dist: Default::default(),
optional_dependencies: Default::default(),
bin: None,
scripts: Default::default(),
deprecated: Default::default(),
}
}

fn reqs<'a>(reqs: impl IntoIterator<Item = &'a str>) -> Vec<PackageReq> {
reqs
.into_iter()
.map(|s| PackageReq::from_str_loose(s).unwrap())
.collect()
}

#[track_caller]
fn assert_snapshot_eq(
a: &SerializedNpmResolutionSnapshot,
b: &SerializedNpmResolutionSnapshot,
) {
let mut a_root_packages = a.root_packages.iter().collect::<Vec<_>>();
a_root_packages.sort();
let mut b_root_packages = b.root_packages.iter().collect::<Vec<_>>();
b_root_packages.sort();
let mut a_packages = a.packages.clone();
a_packages.sort_by(|a, b| a.id.cmp(&b.id));
let mut b_packages = b.packages.clone();
b_packages.sort_by(|a, b| a.id.cmp(&b.id));
assert_eq!(a_root_packages, b_root_packages);
assert_eq!(a_packages, b_packages);
}

#[test]
fn snapshot_subset() {
let a = package("[email protected]", &[("b", "[email protected]"), ("c", "[email protected]")]);
let b = package("[email protected]", &[("d", "[email protected]")]);
let c = package("[email protected]", &[("e", "[email protected]")]);
let d = package("[email protected]", &[]);
let e = package("[email protected]", &[("f", "[email protected]")]);
let f = package("[email protected]", &[("g", "[email protected]")]);
let g = package("[email protected]", &[("e", "[email protected]")]);
let serialized = SerializedNpmResolutionSnapshot {
root_packages: root_pkgs(&[("a@1", "[email protected]"), ("f@1", "[email protected]")]),
packages: vec![a, b, c, d, e.clone(), f.clone(), g.clone()],
};
let snapshot = NpmResolutionSnapshot::new(serialized.into_valid().unwrap());
let subset = snapshot.subset(&reqs(["f@1"])).unwrap();
assert_snapshot_eq(
subset.as_valid_serialized().as_serialized(),
&SerializedNpmResolutionSnapshot {
root_packages: root_pkgs(&[("f@1", "[email protected]")]),
packages: vec![e, f, g],
},
)
}
}