mirror of
https://github.com/sxyazi/yazi.git
synced 2026-07-25 08:41:05 +00:00
Simplify the implementation
This commit is contained in:
parent
14890b0ec1
commit
704baef26c
3 changed files with 48 additions and 158 deletions
|
|
@ -1,46 +1,36 @@
|
|||
use anyhow::{Context, Result, bail};
|
||||
use tokio::fs::{self, remove_dir_all};
|
||||
use yazi_shared::{Xdg, fs::{copy_dir_all, maybe_exists, must_exists, remove_dir_clean}};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::{DiffAction, Package};
|
||||
use crate::package::Git;
|
||||
use anyhow::{Context, Result, bail};
|
||||
use tokio::fs;
|
||||
use yazi_shared::{Xdg, fs::{maybe_exists, must_exists, remove_dir_clean}};
|
||||
|
||||
use super::Package;
|
||||
|
||||
const TRACKER: &str = "DO_NOT_MODIFY_ANYTHING_IN_THIS_DIRECTORY";
|
||||
|
||||
impl Package {
|
||||
pub(super) async fn deploy(&mut self) -> Result<()> {
|
||||
let Some(name) = self.name().map(ToOwned::to_owned) else { bail!("Invalid package url") };
|
||||
let origin = self.local().join(&self.child);
|
||||
let from = self.local().join(&self.child);
|
||||
|
||||
self.header("Deploying package `{name}`")?;
|
||||
self.is_flavor = maybe_exists(&origin.join("flavor.toml")).await;
|
||||
let dest = if self.is_flavor {
|
||||
self.is_flavor = maybe_exists(&from.join("flavor.toml")).await;
|
||||
let to = if self.is_flavor {
|
||||
Xdg::config_dir().join(format!("flavors/{name}"))
|
||||
} else {
|
||||
Xdg::config_dir().join(format!("plugins/{name}"))
|
||||
};
|
||||
|
||||
let tracker = dest.join(TRACKER);
|
||||
if maybe_exists(&dest).await && !must_exists(&tracker).await {
|
||||
let tracker = to.join(TRACKER);
|
||||
if maybe_exists(&to).await && !must_exists(&tracker).await {
|
||||
bail!(
|
||||
"A user package with the same name `{name}` already exists.
|
||||
For safety, please manually delete it from your plugin/flavor directory and re-run the command."
|
||||
);
|
||||
}
|
||||
|
||||
fs::create_dir_all(&dest).await?;
|
||||
|
||||
let mut dirs = &["assets"][..];
|
||||
let mut diff_actions = vec![];
|
||||
if tracker.exists() {
|
||||
let prev_rev = String::from_utf8(fs::read(&tracker).await?)?;
|
||||
|
||||
if prev_rev != self.rev {
|
||||
diff_actions = Git::diff(&self.local().join(&self.child), &prev_rev, dirs).await?;
|
||||
}
|
||||
}
|
||||
|
||||
fs::write(tracker, &self.rev.trim_start_matches('=')).await?;
|
||||
fs::create_dir_all(&to).await?;
|
||||
fs::write(tracker, []).await?;
|
||||
|
||||
let files = if self.is_flavor {
|
||||
&["flavor.toml", "tmtheme.xml", "README.md", "preview.png", "LICENSE", "LICENSE-tmtheme"][..]
|
||||
|
|
@ -49,69 +39,49 @@ For safety, please manually delete it from your plugin/flavor directory and re-r
|
|||
};
|
||||
|
||||
for file in files {
|
||||
let (from, to) = (origin.join(file), dest.join(file));
|
||||
let (from, to) = (from.join(file), to.join(file));
|
||||
|
||||
fs::copy(&from, &to)
|
||||
.await
|
||||
.with_context(|| format!("failed to copy `{}` to `{}`", from.display(), to.display()))?;
|
||||
}
|
||||
|
||||
for action in diff_actions.as_slice() {
|
||||
match action {
|
||||
DiffAction::Add { file } => {
|
||||
let (from, to) = (origin.join(file), dest.join(file));
|
||||
fs::create_dir_all(&to).await?;
|
||||
fs::copy(&from, &to).await.with_context(|| {
|
||||
format!("failed to copy `{}` to `{}`", from.display(), to.display())
|
||||
})?;
|
||||
}
|
||||
DiffAction::Delete { file } => {
|
||||
let file = dest.join(file);
|
||||
fs::remove_file(&file)
|
||||
.await
|
||||
.with_context(|| format!("failed to remove `{}`", file.display()))?;
|
||||
}
|
||||
DiffAction::Rename { old, new } => {
|
||||
let (from, to) = (dest.join(old), dest.join(new));
|
||||
fs::create_dir_all(&to).await?;
|
||||
fs::rename(&from, &to).await.with_context(|| {
|
||||
format!("failed to rename `{}` to `{}`", from.display(), to.display())
|
||||
})?;
|
||||
}
|
||||
DiffAction::Copy { old, new } => {
|
||||
let (from, to) = (dest.join(old), dest.join(new));
|
||||
fs::create_dir_all(&to).await?;
|
||||
fs::copy(&from, &to).await.with_context(|| {
|
||||
format!("failed to copy `{}` to `{}`", from.display(), to.display())
|
||||
})?;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
remove_dir_clean(&dest).await;
|
||||
|
||||
if !diff_actions.is_empty() {
|
||||
dirs = &[];
|
||||
}
|
||||
|
||||
for dir in dirs {
|
||||
let (from, to) = (origin.join(dir), dest.join(dir));
|
||||
|
||||
if !from.exists() {
|
||||
if to.exists() {
|
||||
remove_dir_all(&to)
|
||||
.await
|
||||
.with_context(|| format!("failed to prune no-longer exist folder {}", to.display()))?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
copy_dir_all(&from, &to).await.with_context(|| {
|
||||
format!("failed to copy dir `{}` to `{}`", from.display(), to.display())
|
||||
})?;
|
||||
}
|
||||
Self::deploy_assets(from.join("assets"), to.join("assets")).await?;
|
||||
|
||||
println!("Done!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn deploy_assets(from: PathBuf, to: PathBuf) -> Result<()> {
|
||||
use std::io::ErrorKind::NotFound;
|
||||
|
||||
match fs::read_dir(&to).await {
|
||||
Ok(mut it) => {
|
||||
while let Some(entry) = it.next_entry().await? {
|
||||
fs::remove_file(entry.path())
|
||||
.await
|
||||
.with_context(|| format!("failed to remove `{}`", entry.path().display()))?;
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == NotFound => {}
|
||||
Err(e) => Err(e).context(format!("failed to read `{}`", to.display()))?,
|
||||
};
|
||||
|
||||
remove_dir_clean(&to).await;
|
||||
match fs::read_dir(&from).await {
|
||||
Ok(mut it) => {
|
||||
fs::create_dir_all(&to).await?;
|
||||
while let Some(entry) = it.next_entry().await? {
|
||||
let (src, dist) = (entry.path(), to.join(entry.file_name()));
|
||||
fs::copy(&src, &dist).await.with_context(|| {
|
||||
format!("failed to copy `{}` to `{}`", src.display(), dist.display())
|
||||
})?;
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == NotFound => {}
|
||||
Err(e) => Err(e).context(format!("failed to read `{}`", from.display()))?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,6 @@ use yazi_shared::strip_trailing_newline;
|
|||
|
||||
pub(super) struct Git;
|
||||
|
||||
pub(super) enum DiffAction {
|
||||
Add { file: String },
|
||||
Delete { file: String },
|
||||
Rename { old: String, new: String },
|
||||
Copy { old: String, new: String },
|
||||
}
|
||||
|
||||
impl Git {
|
||||
pub(super) async fn clone(url: &str, path: &Path) -> Result<()> {
|
||||
Self::exec(|c| c.args(["clone", url]).arg(path)).await
|
||||
|
|
@ -49,48 +42,6 @@ impl Git {
|
|||
))
|
||||
}
|
||||
|
||||
pub(super) async fn diff(path: &Path, rev: &str, filter: &[&str]) -> Result<Vec<DiffAction>> {
|
||||
let mut command = Command::new("git");
|
||||
command.current_dir(path).args(["diff", "-M100", "-C100", rev, "origin/HEAD", "--name-status"]);
|
||||
if !filter.is_empty() {
|
||||
command.arg("--");
|
||||
}
|
||||
for f in filter {
|
||||
command.arg(*f);
|
||||
}
|
||||
let output = command.output().await.context("Failed to run git diff")?;
|
||||
|
||||
if !output.status.success() {
|
||||
bail!("git diff failed: {}", output.status);
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8(output.stdout).context("Failed to parse git diff output")?;
|
||||
let mut diff_actions = Vec::new();
|
||||
|
||||
for line in stdout.lines() {
|
||||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||||
if parts.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match parts[0] {
|
||||
"A" | "M" if parts.len() == 2 => {
|
||||
diff_actions.push(DiffAction::Add { file: parts[1].to_string() })
|
||||
}
|
||||
"D" if parts.len() == 2 => {
|
||||
diff_actions.push(DiffAction::Delete { file: parts[1].to_string() })
|
||||
}
|
||||
"R100" if parts.len() == 3 => diff_actions
|
||||
.push(DiffAction::Rename { old: parts[1].to_string(), new: parts[2].to_string() }),
|
||||
"C100" if parts.len() == 3 => diff_actions
|
||||
.push(DiffAction::Copy { old: parts[1].to_string(), new: parts[2].to_string() }),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(diff_actions)
|
||||
}
|
||||
|
||||
async fn exec(f: impl FnOnce(&mut Command) -> &mut Command) -> Result<()> {
|
||||
let status =
|
||||
f(&mut Command::new("git")).status().await.context("Failed to execute `git` command")?;
|
||||
|
|
@ -102,17 +53,3 @@ impl Git {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DiffAction {
|
||||
pub(super) fn is_in(&self, dirs: &[&str]) -> bool {
|
||||
for dir in dirs {
|
||||
match self {
|
||||
DiffAction::Add { file } => return file.contains(dir),
|
||||
DiffAction::Delete { file } => return file.contains(dir),
|
||||
DiffAction::Rename { old: _, new } => return new.contains(dir),
|
||||
DiffAction::Copy { old: _, new } => return new.contains(dir),
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -298,23 +298,6 @@ pub async fn remove_dir_clean(dir: &Path) {
|
|||
fs::remove_dir(dir).await.ok();
|
||||
}
|
||||
|
||||
pub async fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
fs::create_dir_all(&dst).await?;
|
||||
let mut entries = fs::read_dir(src).await?;
|
||||
|
||||
while let Some(entry) = entries.next_entry().await? {
|
||||
let file_type = entry.file_type().await?;
|
||||
let dest_path = dst.as_ref().join(entry.file_name());
|
||||
|
||||
if file_type.is_dir() {
|
||||
Box::pin(copy_dir_all(entry.path(), dest_path)).await?;
|
||||
} else {
|
||||
fs::copy(entry.path(), dest_path).await.ok();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Convert a file mode to a string representation
|
||||
#[cfg(unix)]
|
||||
#[allow(clippy::collapsible_else_if)]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue