feat: delete/uninstall packages

This commit is contained in:
Valter Santos 2025-01-09 16:09:54 +13:00 committed by sxyazi
parent 856f37b5b3
commit 0dbce06a6e
No known key found for this signature in database
8 changed files with 74 additions and 18 deletions

View file

@ -57,6 +57,9 @@ pub(super) struct CommandPack {
/// Add a package.
#[arg(short = 'a', long)]
pub(super) add: Option<String>,
/// Delete a package.
#[arg(short = 'd', long)]
pub(super) delete: Option<String>,
/// Install all packages.
#[arg(short = 'i', long)]
pub(super) install: bool,

View file

@ -73,6 +73,8 @@ async fn run() -> anyhow::Result<()> {
package::Package::load().await?.install(true).await?;
} else if let Some(repo) = cmd.add {
package::Package::load().await?.add(&repo).await?;
} else if let Some(repo) = cmd.delete {
package::Package::load().await?.delete(&repo).await?;
}
}

View file

@ -0,0 +1,26 @@
use anyhow::{Result, bail};
use tokio::fs;
use yazi_fs::must_exists;
use yazi_macro::outln;
use super::Dependency;
impl Dependency {
pub(super) async fn delete(&self) -> Result<()> {
self.header("Deleting package `{name}`")?;
let path = self.deployed_directory();
if must_exists(&path).await {
fs::remove_dir_all(&path).await?;
} else {
bail!(
"The package.toml file states that `{}` exists, but the directory was not found. The entry will be removed from package.toml.",
self.name
);
}
outln!("Done!")?;
Ok(())
}
}

View file

@ -27,6 +27,15 @@ impl Dependency {
.join(format!("{:x}", XxHash3_128::oneshot(self.remote().as_bytes())))
}
#[inline]
pub(super) fn deployed_directory(&self) -> PathBuf {
return if self.is_flavor {
Xdg::config_dir().join(format!("flavors/{}", self.name))
} else {
Xdg::config_dir().join(format!("plugins/{}", self.name))
};
}
#[inline]
pub(super) fn remote(&self) -> String {
// Support more Git hosting services in the future

View file

@ -2,7 +2,7 @@ use std::path::PathBuf;
use anyhow::{Context, Result, bail};
use tokio::fs;
use yazi_fs::{Xdg, copy_and_seal, maybe_exists, remove_dir_clean};
use yazi_fs::{copy_and_seal, maybe_exists, remove_dir_clean};
use yazi_macro::outln;
use super::Dependency;
@ -13,11 +13,8 @@ impl Dependency {
self.header("Deploying package `{name}`")?;
self.is_flavor = maybe_exists(&from.join("flavor.toml")).await;
let to = if self.is_flavor {
Xdg::config_dir().join(format!("flavors/{}", self.name))
} else {
Xdg::config_dir().join(format!("plugins/{}", self.name))
};
let to = self.deployed_directory();
if maybe_exists(&to).await && self.hash != self.hash().await? {
bail!(

View file

@ -1,17 +1,13 @@
use anyhow::{Context, Result};
use tokio::fs;
use twox_hash::XxHash3_128;
use yazi_fs::{Xdg, ok_or_not_found};
use yazi_fs::ok_or_not_found;
use super::Dependency;
impl Dependency {
pub(crate) async fn hash(&self) -> Result<String> {
let dir = if self.is_flavor {
Xdg::config_dir().join(format!("flavors/{}", self.name))
} else {
Xdg::config_dir().join(format!("plugins/{}", self.name))
};
let dir = self.deployed_directory();
let files = if self.is_flavor {
&[

View file

@ -1,6 +1,6 @@
#![allow(clippy::module_inception)]
yazi_macro::mod_flat!(add dependency deploy git hash install package upgrade);
yazi_macro::mod_flat!(add delete dependency deploy git hash install package upgrade);
use anyhow::Context;
use yazi_fs::Xdg;

View file

@ -25,11 +25,10 @@ impl Package {
pub(crate) async fn add(&mut self, use_: &str) -> Result<()> {
let mut dep = Dependency::from_str(use_)?;
if self.plugins.iter().any(|d| d.parent == dep.parent && d.child == dep.child) {
bail!("Plugin `{}` already exists in package.toml", dep.name);
}
if self.flavors.iter().any(|d| d.parent == dep.parent && d.child == dep.child) {
bail!("Flavor `{}` already exists in package.toml", dep.name);
if let Some(existing_dep) = self.find_dep_in_package(&dep) {
let package_type = if existing_dep.is_flavor { "Flavor" } else { "Plugin" };
bail!("{} `{}` already exists in package.toml", package_type, dep.name)
}
dep.add().await?;
@ -43,6 +42,25 @@ impl Package {
create_and_seal(&Self::toml(), s.as_bytes()).await.context("Failed to write package.toml")
}
pub(crate) async fn delete(&mut self, use_: &str) -> Result<()> {
let dep_to_find = Dependency::from_str(use_)?;
let dep = match self.find_dep_in_package(&dep_to_find) {
Some(d) => d,
None => bail!("`{}` was not found in package.toml", use_),
};
dep.delete().await?;
if dep.is_flavor {
self.flavors.retain(|f| f.use_ != use_);
} else {
self.plugins.retain(|f| f.use_ != use_);
}
let s = toml::to_string_pretty(self)?;
create_and_seal(&Self::toml(), s.as_bytes()).await.context("Failed to write package.toml")
}
pub(crate) async fn install(&mut self, upgrade: bool) -> Result<()> {
for d in &mut self.plugins {
if upgrade {
@ -165,6 +183,11 @@ impl Package {
create_and_seal(&Self::toml(), s.as_bytes()).await.context("Failed to write package.toml")
}
#[inline]
fn find_dep_in_package(&self, dep: &Dependency) -> Option<&Dependency> {
return self.plugins.iter().chain(self.flavors.iter()).find(|d| d.use_ == dep.use_);
}
#[inline]
fn toml() -> PathBuf { Xdg::config_dir().join("package.toml") }
}