Skip to content

Commit

Permalink
feat!(update/remove): parallelize + use builder pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
mrcjkb committed Jan 2, 2025
1 parent 6ed86df commit 1b890a6
Show file tree
Hide file tree
Showing 11 changed files with 408 additions and 162 deletions.
13 changes: 8 additions & 5 deletions rocks-bin/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rocks_lib::{
package::{PackageName, PackageReq},
progress::MultiProgress,
rockspec::Rockspec,
tree::Tree,
tree::{RockMatches, Tree},
};

#[derive(Args, Default)]
Expand Down Expand Up @@ -70,14 +70,14 @@ pub async fn build(data: Build, config: Config) -> Result<()> {

let tree = Tree::new(config.tree().clone(), lua_version)?;

let build_behaviour = match tree.has_rock_and(
let build_behaviour = match tree.match_rocks_and(
&PackageReq::new(
rockspec.package.to_string(),
Some(rockspec.version.to_string()),
)?,
|rock| pin == rock.pinned(),
) {
Some(_) if !data.force => {
)? {
RockMatches::Single(_) | RockMatches::Many(_) if !data.force => {
if Confirm::new(&format!(
"Package {} already exists. Overwrite?",
rockspec.package,
Expand Down Expand Up @@ -106,7 +106,10 @@ pub async fn build(data: Build, config: Config) -> Result<()> {

let dependencies_to_install = dependencies
.into_iter()
.filter(|req| tree.has_rock(req).is_none())
.filter(|req| {
tree.match_rocks(req)
.is_ok_and(|rock_match| rock_match.is_found())
})
.map(|dep| (build_behaviour, dep.to_owned()))
.collect_vec();

Expand Down
2 changes: 1 addition & 1 deletion rocks-bin/src/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub async fn info(data: Info, config: Config) -> Result<()> {

bar.map(|b| b.finish_and_clear());

if tree.has_rock(&data.package).is_some() {
if tree.match_rocks(&data.package)?.is_found() {
println!("Currently installed in {}", tree.root().display());
}

Expand Down
32 changes: 17 additions & 15 deletions rocks-bin/src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rocks_lib::{
operations,
package::PackageReq,
progress::MultiProgress,
tree::Tree,
tree::{RockMatches, Tree},
};

#[derive(clap::Args)]
Expand All @@ -35,21 +35,23 @@ pub async fn install(data: Install, config: Config) -> Result<()> {
.package_req
.into_iter()
.filter_map(|req| {
let build_behaviour: Option<BuildBehaviour> =
match tree.has_rock_and(&req, |rock| pin == rock.pinned()) {
Some(_) if !data.force => {
if Confirm::new(&format!("Package {} already exists. Overwrite?", req))
.with_default(false)
.prompt()
.expect("Error prompting for reinstall")
{
Some(BuildBehaviour::Force)
} else {
None
}
let build_behaviour: Option<BuildBehaviour> = match tree
.match_rocks_and(&req, |rock| pin == rock.pinned())
.expect("unable to get tree data")
{
RockMatches::Single(_) | RockMatches::Many(_) if !data.force => {
if Confirm::new(&format!("Package {} already exists. Overwrite?", req))
.with_default(false)
.prompt()
.expect("Error prompting for reinstall")
{
Some(BuildBehaviour::Force)
} else {
None
}
_ => Some(BuildBehaviour::from(data.force)),
};
}
_ => Some(BuildBehaviour::from(data.force)),
};
build_behaviour.map(|it| (it, req))
})
.collect_vec();
Expand Down
13 changes: 8 additions & 5 deletions rocks-bin/src/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use eyre::Result;
use rocks_lib::lockfile::PinnedState;
use rocks_lib::operations;
use rocks_lib::package::PackageSpec;
use rocks_lib::tree::RockMatches;
use rocks_lib::{
config::{Config, LuaVersion},
tree::Tree,
Expand All @@ -17,11 +18,13 @@ pub struct ChangePin {
pub fn set_pinned_state(data: ChangePin, config: Config, pin: PinnedState) -> Result<()> {
let tree = Tree::new(config.tree().clone(), LuaVersion::from(&config)?)?;

if let Some(mut rock) = tree.has_rock_and(&data.package.clone().into_package_req(), |package| {
match tree.match_rocks_and(&data.package.clone().into_package_req(), |package| {
pin != package.pinned()
}) {
Ok(operations::set_pinned_state(&mut rock, &tree, pin)?)
} else {
Err(eyre!("Rock {} not found!", data.package))
})? {
RockMatches::Single(mut rock) => Ok(operations::set_pinned_state(&mut rock, &tree, pin)?),
RockMatches::Many(_) => {
todo!("Add an error here about many conflicting types and to use `all:`")
}
RockMatches::NotFound(_) => Err(eyre!("Rock {} not found!", data.package)),
}
}
77 changes: 49 additions & 28 deletions rocks-bin/src/remove.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,64 @@
use std::io;

use clap::Args;
use eyre::Result;
use eyre::{eyre, Result};
use itertools::Itertools;
use rocks_lib::{
config::{Config, LuaVersion},
package::{PackageName, PackageSpec, PackageVersion},
progress::{MultiProgress, Progress},
remote_package_db::RemotePackageDB,
tree::Tree,
operations,
package::PackageReq,
tree::{RockMatches, Tree},
};

#[derive(Args)]
pub struct Remove {
/// The name of the rock to remove.
name: PackageName,
/// The name of the version to remove.
version: Option<PackageVersion>,
/// The package or packages to remove.
packages: Vec<PackageReq>,
}

pub async fn remove(remove_args: Remove, config: Config) -> Result<()> {
let package_db = RemotePackageDB::from_config(&config).await?;
let tree = Tree::new(config.tree().clone(), LuaVersion::from(&config)?)?;

let target_version = remove_args
.version
.or(package_db.latest_version(&remove_args.name).cloned())
.unwrap();
let package_matches = remove_args
.packages
.iter()
.map(|package_req| tree.match_rocks(package_req))
.try_collect::<_, Vec<_>, io::Error>()?;

let tree = Tree::new(config.tree().clone(), LuaVersion::from(&config)?)?;
let (packages, nonexistent_packages, duplicate_packages) = package_matches.into_iter().fold(
(Vec::new(), Vec::new(), Vec::new()),
|(mut p, mut n, mut d), rock_match| {
match rock_match {
RockMatches::NotFound(req) => n.push(req),
RockMatches::Single(package) => p.push(package),
RockMatches::Many(packages) => d.extend(packages),
};

(p, n, d)
},
);

match tree.has_rock(
&PackageSpec::new(remove_args.name.clone(), target_version.clone()).into_package_req(),
) {
Some(package) => Ok(rocks_lib::operations::remove(
package,
&config,
&Progress::Progress(MultiProgress::new().new_bar()),
)
.await?),
None => {
eprintln!("Could not find {}@{}", remove_args.name, target_version);
Ok(())
}
if !nonexistent_packages.is_empty() {
// TODO(vhyrro): Render this in the form of a tree.
return Err(eyre!(
"The following packages were not found: {:#?}",
nonexistent_packages
));
}

if !duplicate_packages.is_empty() {
// TODO(vhyrro): Greatly expand on this error message, notifying the user how it works and
// how to fix it.
return Err(eyre!(
"Multiple packages satisfying your version requirements were found. {:#?}",
duplicate_packages,
));
}

operations::Remove::new(&config)
.packages(packages)
.remove()
.await?;

Ok(())
}
32 changes: 12 additions & 20 deletions rocks-bin/src/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use eyre::Result;
use rocks_lib::config::LuaVersion;
use rocks_lib::lockfile::PinnedState;
use rocks_lib::progress::{MultiProgress, ProgressBar};
use rocks_lib::remote_package_db::RemotePackageDB;
use rocks_lib::{config::Config, operations, package::PackageReq, tree::Tree};
use rocks_lib::{config::Config, operations, tree::Tree};

#[derive(Args)]
pub struct Update {}
Expand All @@ -14,26 +13,19 @@ pub async fn update(config: Config) -> Result<()> {
progress.map(|p| p.add(ProgressBar::from("🔎 Looking for updates...".to_string())));

let tree = Tree::new(config.tree().clone(), LuaVersion::from(&config)?)?;

let lockfile = tree.lockfile()?;
let rocks = lockfile.rocks();
let package_db = RemotePackageDB::from_config(&config).await?;

for package in rocks.values() {
if package.pinned() == PinnedState::Unpinned {
operations::update(
package.clone(),
PackageReq::new(
package.name().to_string(),
package.constraint().to_string_opt(),
)?,
package_db.clone(),
&config,
progress.clone(),
)
.await?;
}
}
operations::Update::new(&config)
.packages(
lockfile
.rocks()
.values()
.filter(|package| package.pinned() == PinnedState::Unpinned)
.map(|package| (package.clone(), package.to_package().into_package_req())),
)
.progress(progress)
.update()
.await?;

Ok(())
}
2 changes: 1 addition & 1 deletion rocks-lib/src/luarocks_installation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl LuaRocksInstallation {
let mut lockfile = self.tree.lockfile()?;
let luarocks_req =
PackageReq::new("luarocks".into(), Some(LUAROCKS_VERSION.into())).unwrap();
if self.tree.has_rock(&luarocks_req).is_none() {
if !self.tree.match_rocks(&luarocks_req)?.is_found() {
let rockspec = Rockspec::new(LUAROCKS_ROCKSPEC).unwrap();
let pkg = Build::new(rockspec, &self.config, progress)
.constraint(LockConstraint::Constrained(
Expand Down
94 changes: 79 additions & 15 deletions rocks-lib/src/operations/remove.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use std::io;
use std::sync::Arc;

use crate::config::{LuaVersion, LuaVersionUnset};
use crate::lockfile::LocalPackage;
use crate::progress::{Progress, ProgressBar};
use crate::progress::{MultiProgress, Progress, ProgressBar};
use crate::{config::Config, tree::Tree};
use futures::future::join_all;
use itertools::Itertools;
use thiserror::Error;

#[derive(Error, Debug)]
Expand All @@ -13,29 +16,90 @@ pub enum RemoveError {
Io(#[from] io::Error),
}

pub struct Remove<'a> {
config: &'a Config,
packages: Vec<LocalPackage>,
progress: Option<Arc<Progress<MultiProgress>>>,
}

/// A rocks package remover.
/// Can remove multiple packages in parallel.
impl<'a> Remove<'a> {
/// Construct a new rocks package remover.
pub fn new(config: &'a Config) -> Self {
Self {
config,
packages: Vec::new(),
progress: None,
}
}

/// Add packages to remove.
pub fn packages<I>(self, packages: I) -> Self
where
I: IntoIterator<Item = LocalPackage>,
{
Self {
packages: self.packages.into_iter().chain(packages).collect_vec(),
..self
}
}

/// Add a package to the set of packages to remove.
pub fn package(self, package: LocalPackage) -> Self {
self.packages(std::iter::once(package))
}

/// Pass a `MultiProgress` to this installer.
/// By default, a new one will be created.
pub fn progress(self, progress: Arc<Progress<MultiProgress>>) -> Self {
Self {
progress: Some(progress),
..self
}
}

/// Remove the packages.
pub async fn remove(self) -> Result<(), RemoveError> {
let progress = match self.progress {
Some(p) => p,
None => MultiProgress::new_arc(),
};
remove(self.packages, self.config, &Arc::clone(&progress)).await
}
}

// TODO: Remove dependencies recursively too!
pub async fn remove(
package: LocalPackage,
async fn remove(
packages: Vec<LocalPackage>,
config: &Config,
progress: &Progress<ProgressBar>,
progress: &Progress<MultiProgress>,
) -> Result<(), RemoveError> {
progress.map(|p| {
p.set_message(format!(
"🗑️ Removing {}@{}",
package.name(),
package.version()
))
});

remove_impl(package, config).await
join_all(packages.into_iter().map(|package| {
let _bar = progress.map(|p| {
p.add(ProgressBar::from(format!(
"🗑️ Removing {}@{}",
package.name(),
package.version()
)))
});

let config = config.clone();

tokio::spawn(remove_package(package, config))
}))
.await;

Ok(())
}

async fn remove_impl(package: LocalPackage, config: &Config) -> Result<(), RemoveError> {
let tree = Tree::new(config.tree().clone(), LuaVersion::from(config)?)?;
async fn remove_package(package: LocalPackage, config: Config) -> Result<(), RemoveError> {
let tree = Tree::new(config.tree().clone(), LuaVersion::from(&config)?)?;

tree.lockfile()?.remove(&package);

std::fs::remove_dir_all(tree.root_for(&package))?;
tokio::fs::remove_dir_all(tree.root_for(&package)).await?;

Ok(())
}
Loading

0 comments on commit 1b890a6

Please sign in to comment.