diff --git a/rocks-lib/src/lockfile/mod.rs b/rocks-lib/src/lockfile/mod.rs index d12f8b8b..3566836b 100644 --- a/rocks-lib/src/lockfile/mod.rs +++ b/rocks-lib/src/lockfile/mod.rs @@ -9,7 +9,8 @@ use ssri::Integrity; use thiserror::Error; use crate::package::{ - PackageName, PackageReq, PackageSpec, PackageVersion, PackageVersionReq, PackageVersionReqError, + PackageName, PackageReq, PackageSpec, PackageVersion, PackageVersionReq, + PackageVersionReqError, RemotePackageTypeFilterSpec, }; use crate::remote_package_source::RemotePackageSource; @@ -184,7 +185,7 @@ impl LocalPackageSpec { #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct LocalPackage { pub(crate) spec: LocalPackageSpec, - source: RemotePackageSource, + pub(crate) source: RemotePackageSource, hashes: LocalPackageHashes, } @@ -524,12 +525,27 @@ impl Lockfile { .into_group_map() } - pub(crate) fn has_rock(&self, req: &PackageReq) -> Option { + pub(crate) fn has_rock( + &self, + req: &PackageReq, + filter: Option, + ) -> Option { self.list() .get(req.name()) .map(|packages| { packages .iter() + .filter(|package| match &filter { + Some(filter_spec) => match package.source { + RemotePackageSource::LuarocksRockspec(_) => filter_spec.rockspec, + RemotePackageSource::LuarocksSrcRock(_) => filter_spec.src, + RemotePackageSource::LuarocksBinaryRock(_) => filter_spec.binary, + RemotePackageSource::RockspecContent(_) => true, + #[cfg(test)] + RemotePackageSource::Test => unimplemented!(), + }, + None => true, + }) .rev() .find(|package| req.version_req().matches(package.version())) })? diff --git a/rocks-lib/src/manifest/mod.rs b/rocks-lib/src/manifest/mod.rs index 8ecabdf0..0037e3ca 100644 --- a/rocks-lib/src/manifest/mod.rs +++ b/rocks-lib/src/manifest/mod.rs @@ -141,14 +141,6 @@ impl ManifestMetadata { self.repository.contains_key(rock_name) } - pub fn latest_version(&self, rock_name: &PackageName) -> Option<&PackageVersion> { - if !self.has_rock(rock_name) { - return None; - } - - self.repository[rock_name].keys().sorted().last() - } - pub fn latest_match( &self, lua_package_req: &PackageReq, @@ -241,7 +233,9 @@ impl Manifest { pub fn metadata(&self) -> &ManifestMetadata { &self.metadata } - pub fn search( + + /// Find a package that matches the requirement, returning the latest match + pub fn find( &self, package_req: &PackageReq, filter: Option, diff --git a/rocks-lib/src/operations/resolve.rs b/rocks-lib/src/operations/resolve.rs index 542676f6..7acb6d3d 100644 --- a/rocks-lib/src/operations/resolve.rs +++ b/rocks-lib/src/operations/resolve.rs @@ -39,7 +39,8 @@ pub(crate) async fn get_all_dependencies( .into_iter() // Exclude packages that are already installed .filter(|(build_behaviour, package)| { - build_behaviour == &BuildBehaviour::Force || lockfile.has_rock(package).is_none() + build_behaviour == &BuildBehaviour::Force + || lockfile.has_rock(package, None).is_none() }) .map(|(build_behaviour, package)| { let config = config.clone(); diff --git a/rocks-lib/src/package/mod.rs b/rocks-lib/src/package/mod.rs index 2fdc64c7..1824d176 100644 --- a/rocks-lib/src/package/mod.rs +++ b/rocks-lib/src/package/mod.rs @@ -204,6 +204,14 @@ impl From for PackageReq { } } +impl From for PackageReq { + fn from(name: PackageName) -> Self { + Self { + name, + version_req: PackageVersionReq::default(), + } + } +} #[cfg(feature = "lua")] impl mlua::UserData for PackageReq { fn add_methods>(methods: &mut M) { diff --git a/rocks-lib/src/package/outdated.rs b/rocks-lib/src/package/outdated.rs index 35119b16..491f7d1c 100644 --- a/rocks-lib/src/package/outdated.rs +++ b/rocks-lib/src/package/outdated.rs @@ -28,8 +28,8 @@ impl PackageSpec { .latest_version(&self.name) .ok_or_else(|| RockNotFound(self.name.clone()))?; - if self.version < *latest_version { - Ok(Some(latest_version.to_owned())) + if self.version < latest_version { + Ok(Some(latest_version)) } else { Ok(None) } diff --git a/rocks-lib/src/remote_package_db/mod.rs b/rocks-lib/src/remote_package_db/mod.rs index dd2b59dc..ea0b1589 100644 --- a/rocks-lib/src/remote_package_db/mod.rs +++ b/rocks-lib/src/remote_package_db/mod.rs @@ -1,5 +1,8 @@ +use std::collections::HashMap; + use crate::{ config::Config, + lockfile::Lockfile, manifest::{Manifest, ManifestError}, package::{ PackageName, PackageReq, PackageSpec, PackageVersion, RemotePackage, @@ -7,11 +10,17 @@ use crate::{ }, progress::{Progress, ProgressBar}, }; -use itertools::Itertools as _; +use itertools::Itertools; use thiserror::Error; #[derive(Clone)] -pub struct RemotePackageDB(Vec); +pub struct RemotePackageDB(Impl); + +#[derive(Clone)] +enum Impl { + LuarocksManifests(Vec), + Lockfile(Lockfile), +} #[derive(Error, Debug)] pub enum RemotePackageDBError { @@ -37,80 +46,108 @@ impl RemotePackageDB { manifests.push(manifest); } manifests.push(Manifest::from_config(config.server().clone(), config).await?); - Ok(Self(manifests)) + Ok(Self(Impl::LuarocksManifests(manifests))) } - /// Find a package that matches the requirement + /// Find a remote package that matches the requirement, returning the latest match. pub(crate) fn find( &self, package_req: &PackageReq, filter: Option, progress: &Progress, ) -> Result { - let result = self.0.iter().find_map(|manifest| { - progress.map(|p| p.set_message(format!("🔎 Searching {}", &manifest.server_url()))); - manifest.search(package_req, filter.clone()) - }); + let result = match &self.0 { + Impl::LuarocksManifests(manifests) => manifests.iter().find_map(|manifest| { + progress.map(|p| p.set_message(format!("🔎 Searching {}", &manifest.server_url()))); + manifest.find(package_req, filter.clone()) + }), + Impl::Lockfile(lockfile) => { + lockfile.has_rock(package_req, filter).map(|local_package| { + RemotePackage::new( + PackageSpec::new(local_package.spec.name, local_package.spec.version), + local_package.source, + ) + }) + } + }; match result { Some(package) => Ok(package), None => Err(SearchError::RockNotFound(package_req.clone())), } } - /// Search for all packages that match the requirement + /// Search for all packages that match the requirement. pub fn search(&self, package_req: &PackageReq) -> Vec<(&PackageName, Vec<&PackageVersion>)> { - self.0 - .iter() - .flat_map(|manifest| { - manifest - .metadata() - .repository - .iter() - .filter_map(|(name, elements)| { - if name.to_string().contains(&package_req.name().to_string()) { - Some(( - name, - elements - .keys() - .filter(|version| package_req.version_req().matches(version)) - .sorted_by(|a, b| Ord::cmp(b, a)) - .collect_vec(), - )) - } else { - None - } - }) - }) - .collect() + match &self.0 { + Impl::LuarocksManifests(manifests) => manifests + .iter() + .flat_map(|manifest| { + manifest + .metadata() + .repository + .iter() + .filter_map(|(name, elements)| { + if name.to_string().contains(&package_req.name().to_string()) { + Some(( + name, + elements + .keys() + .filter(|version| { + package_req.version_req().matches(version) + }) + .sorted_by(|a, b| Ord::cmp(b, a)) + .collect_vec(), + )) + } else { + None + } + }) + }) + .collect(), + Impl::Lockfile(lockfile) => lockfile + .rocks() + .iter() + .filter_map(|(_, package)| { + // NOTE: This doesn't group packages by name, but we don't care for now, + // as we shouldn't need to use this function with a lockfile. + let name = package.name(); + if name.to_string().contains(&package_req.name().to_string()) { + Some((name, vec![package.version()])) + } else { + None + } + }) + .collect_vec(), + } } - pub fn latest_version(&self, rock_name: &PackageName) -> Option<&PackageVersion> { - self.0 - .iter() - .filter_map(|manifest| manifest.metadata().latest_version(rock_name)) - .sorted() - .last() + /// Find the latest version for a package by name. + pub(crate) fn latest_version(&self, rock_name: &PackageName) -> Option { + self.latest_match(&rock_name.clone().into(), None) + .map(|result| result.version().clone()) } + /// Find the latest package that matches the requirement. pub fn latest_match( &self, package_req: &PackageReq, filter: Option, ) -> Option { - self.0 - .iter() - .filter_map(|manifest| { - manifest - .metadata() - .latest_match(package_req, filter.clone()) - .map(|x| x.0) - }) - .last() + match self.find(package_req, filter, &Progress::NoProgress) { + Ok(result) => Some(result.package), + Err(_) => None, + } } } impl From for RemotePackageDB { fn from(manifest: Manifest) -> Self { - RemotePackageDB(vec![manifest]) + Self(Impl::LuarocksManifests(vec![manifest])) + } +} + +impl From for RemotePackageDB { + fn from(lockfile: Lockfile) -> Self { + Self(Impl::Lockfile(lockfile)) } }