Skip to content

Commit

Permalink
feat: rocks which command
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcjkb committed Jan 15, 2025
1 parent c213872 commit 15e6221
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 20 deletions.
1 change: 1 addition & 0 deletions rocks-lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod remote_package_db;
pub mod rockspec;
pub mod tree;
pub mod upload;
pub mod which;

pub(crate) mod remote_package_source;

Expand Down
13 changes: 13 additions & 0 deletions rocks-lib/src/lockfile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -524,6 +524,19 @@ impl<P: LockfilePermissions> Lockfile<P> {
.cloned()
}

/// Find all rocks that match the requirement
pub(crate) fn find_rocks(&self, req: &PackageReq) -> Vec<LocalPackageId> {
match self.list().get(req.name()) {
Some(packages) => packages
.iter()
.rev()
.filter(|package| req.version_req().matches(package.version()))
.map(|package| package.id())
.collect_vec(),
None => Vec::default(),
}
}

/// Validate the integrity of an installed package with the entry in this lockfile.
pub(crate) fn validate_integrity(
&self,
Expand Down
14 changes: 11 additions & 3 deletions rocks-lib/src/rockspec/build/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,22 @@ pub struct LuaModule(String);

impl LuaModule {
pub fn to_lua_path(&self) -> PathBuf {
self.to_pathbuf(".lua")
self.to_file_path(".lua")
}

pub fn to_lua_init_path(&self) -> PathBuf {
self.to_path_buf().join("init.lua")
}

pub fn to_lib_path(&self) -> PathBuf {
self.to_pathbuf(&format!(".{}", lua_lib_extension()))
self.to_file_path(&format!(".{}", lua_lib_extension()))
}

fn to_path_buf(&self) -> PathBuf {
PathBuf::from(self.0.replace('.', std::path::MAIN_SEPARATOR_STR))
}

fn to_pathbuf(&self, extension: &str) -> PathBuf {
fn to_file_path(&self, extension: &str) -> PathBuf {
PathBuf::from(self.0.replace('.', std::path::MAIN_SEPARATOR_STR) + extension)
}

Expand Down
23 changes: 6 additions & 17 deletions rocks-lib/src/tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,12 @@ impl Tree {
}

pub fn match_rocks(&self, req: &PackageReq) -> io::Result<RockMatches> {
match self.list()?.get(req.name()) {
Some(packages) => {
let mut found_packages = packages
.iter()
.rev()
.filter(|package| req.version_req().matches(package.version()))
.map(|package| package.id())
.collect_vec();

Ok(match found_packages.len() {
0 => RockMatches::NotFound(req.clone()),
1 => RockMatches::Single(found_packages.pop().unwrap()),
2.. => RockMatches::Many(found_packages),
})
}
None => Ok(RockMatches::NotFound(req.clone())),
}
let mut found_packages = self.lockfile()?.find_rocks(req);
Ok(match found_packages.len() {
0 => RockMatches::NotFound(req.clone()),
1 => RockMatches::Single(found_packages.pop().unwrap()),
2.. => RockMatches::Many(found_packages),
})
}

pub fn match_rocks_and<F>(&self, req: &PackageReq, filter: F) -> io::Result<RockMatches>
Expand Down
151 changes: 151 additions & 0 deletions rocks-lib/src/which/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use std::{io, path::PathBuf};

use bon::{builder, Builder};
use itertools::Itertools;
use thiserror::Error;

use crate::{
config::{Config, LuaVersion, LuaVersionUnset},
package::PackageReq,
rockspec::LuaModule,
tree::Tree,
};

/// A rocks module finder.
#[derive(Builder)]
#[builder(start_fn = new, finish_fn(name = _build, vis = ""))]
pub struct Which<'a> {
#[builder(start_fn)]
module: LuaModule,
#[builder(start_fn)]
config: &'a Config,
#[builder(field)]
packages: Vec<PackageReq>,
}

impl<State> WhichBuilder<'_, State>
where
State: which_builder::State,
{
pub fn package(mut self, package: PackageReq) -> Self {
self.packages.push(package);
self
}

pub fn packages(mut self, packages: impl IntoIterator<Item = PackageReq>) -> Self {
self.packages.extend(packages);
self
}

pub fn search(self) -> Result<PathBuf, WhichError>
where
State: which_builder::IsComplete,
{
do_search(self._build())
}
}

#[derive(Error, Debug)]
pub enum WhichError {
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
LuaVersionUnset(#[from] LuaVersionUnset),
#[error("lua module {0} not found.")]
ModuleNotFound(LuaModule),
}

fn do_search(which: Which<'_>) -> Result<PathBuf, WhichError> {
let config = which.config;
let tree = Tree::new(config.tree().clone(), LuaVersion::from(config)?)?;
let lockfile = tree.lockfile()?;
let local_packages = if which.packages.is_empty() {
lockfile
.list()
.into_iter()
.flat_map(|(_, pkgs)| pkgs)
.collect_vec()
} else {
which
.packages
.iter()
.flat_map(|req| {
lockfile
.find_rocks(req)
.into_iter()
.map(|id| lockfile.get(&id).unwrap())
.cloned()
.collect_vec()
})
.collect_vec()
};
local_packages
.into_iter()
.filter_map(|pkg| {
let rock_layout = tree.rock_layout(&pkg);
let lib_path = rock_layout.lib.join(which.module.to_lib_path());
if lib_path.is_file() {
return Some(lib_path);
}
let lua_path = rock_layout.src.join(which.module.to_lua_path());
if lua_path.is_file() {
return Some(lua_path);
}
let lua_path = rock_layout.src.join(which.module.to_lua_init_path());
if lua_path.is_file() {
return Some(lua_path);
}
None
})
.next()
.ok_or(WhichError::ModuleNotFound(which.module))
}

#[cfg(test)]
mod test {
use super::*;
use crate::config::{ConfigBuilder, LuaVersion};
use assert_fs::prelude::PathCopy;
use std::{path::PathBuf, str::FromStr};

#[tokio::test]
async fn test_which() {
let tree_path =
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/sample-tree");
let temp = assert_fs::TempDir::new().unwrap();
temp.copy_from(&tree_path, &["**"]).unwrap();
let tree_path = temp.to_path_buf();
let config = ConfigBuilder::new()
.tree(Some(tree_path.clone()))
.lua_version(Some(LuaVersion::Lua51))
.build()
.unwrap();

let result = Which::new(LuaModule::from_str("foo.bar").unwrap(), &config)
.search()
.unwrap();
assert_eq!(result.file_name().unwrap().to_string_lossy(), "bar.lua");
assert_eq!(
result
.parent()
.unwrap()
.file_name()
.unwrap()
.to_string_lossy(),
"foo"
);
let result = Which::new(LuaModule::from_str("bat.baz").unwrap(), &config)
.search()
.unwrap();
assert_eq!(result.file_name().unwrap().to_string_lossy(), "baz.so");
assert_eq!(
result
.parent()
.unwrap()
.file_name()
.unwrap()
.to_string_lossy(),
"bat"
);
}
}

0 comments on commit 15e6221

Please sign in to comment.