From 4708e7627502905f7163536af333d545f8814ff3 Mon Sep 17 00:00:00 2001 From: sxyazi Date: Mon, 14 Aug 2023 18:03:52 +0800 Subject: [PATCH] .. --- config/src/boot/boot.rs | 4 ++-- core/src/manager/manager.rs | 30 ++++++++++++----------------- shared/src/fs.rs | 38 +++++++++++++++++++++++++++++++------ 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/config/src/boot/boot.rs b/config/src/boot/boot.rs index 57bcf326..94a938e5 100644 --- a/config/src/boot/boot.rs +++ b/config/src/boot/boot.rs @@ -48,8 +48,8 @@ impl Boot { } #[inline] - pub fn tmpfile(&self) -> PathBuf { + pub fn tmpfile(&self, prefix: &str) -> PathBuf { let nanos = SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos(); - self.cache_dir.join(format!("{:x}", Md5::new_with_prefix(nanos.to_le_bytes()).finalize())) + self.cache_dir.join(format!("{prefix}-{}", nanos / 1000)) } } diff --git a/core/src/manager/manager.rs b/core/src/manager/manager.rs index b4a6150c..4efc275a 100644 --- a/core/src/manager/manager.rs +++ b/core/src/manager/manager.rs @@ -2,7 +2,7 @@ use std::{collections::{BTreeMap, BTreeSet, HashMap, HashSet}, env, ffi::OsStr, use anyhow::{bail, Error, Result}; use config::{open::Opener, BOOT, OPEN}; -use shared::{in_same_root, Defer, Term, MIME_DIR}; +use shared::{max_common_root, Defer, Term, MIME_DIR}; use tokio::{fs::{self, OpenOptions}, io::{AsyncReadExt, AsyncWriteExt}}; use super::{PreviewData, Tab, Tabs, Watcher}; @@ -224,18 +224,13 @@ impl Manager { pub fn bulk_rename(&self) -> bool { let mut old: Vec<_> = self.selected().iter().map(|&f| f.path()).collect(); - if old.is_empty() { - return false; - } - let root = in_same_root(&old); - if let Some(ref root) = root { - old = old.into_iter().map(|p| p.strip_prefix(root).unwrap().to_owned()).collect(); - } + let root = max_common_root(&old); + old = old.into_iter().map(|p| p.strip_prefix(&root).unwrap().to_owned()).collect(); - let tmp = BOOT.tmpfile(); + let tmp = BOOT.tmpfile("bulk"); tokio::spawn(async move { - let Some(opener) = OPEN.block_opener("bulk-rename.txt", "text/plain") else { + let Some(opener) = OPEN.block_opener("bulk.txt", "text/plain") else { bail!("No opener for bulk rename"); }; @@ -246,7 +241,10 @@ impl Manager { } let _guard = BLOCKER.acquire().await.unwrap(); - let _defer = Defer::new(|| Event::Stop(false, None).emit()); + let _defer = Defer::new(|| { + Event::Stop(false, None).emit(); + tokio::spawn(fs::remove_file(tmp.clone())) + }); emit!(Stop(true)).await; let mut child = external::shell(ShellOpt { @@ -256,18 +254,14 @@ impl Manager { })?; child.wait().await?; - let new: Vec<_> = fs::read_to_string(tmp).await?.lines().map(|l| l.into()).collect(); + let new: Vec<_> = fs::read_to_string(&tmp).await?.lines().map(|l| l.into()).collect(); Self::bulk_rename_do(root, old, new).await }); false } - async fn bulk_rename_do( - root: Option, - old: Vec, - new: Vec, - ) -> Result<()> { + async fn bulk_rename_do(root: PathBuf, old: Vec, new: Vec) -> Result<()> { Term::clear()?; if old.len() != new.len() { println!("Number of old and new differ, press ENTER to exit"); @@ -282,7 +276,7 @@ impl Manager { stdout().write_all(b" -> ")?; stdout().write_all(n.as_os_str().as_bytes())?; stdout().write_all(b"\n")?; - todo.push(if let Some(ref root) = root { (root.join(o), root.join(n)) } else { (o, n) }); + todo.push((root.join(o), root.join(n))); } } if todo.is_empty() { diff --git a/shared/src/fs.rs b/shared/src/fs.rs index 3e2e8793..bc62d97a 100644 --- a/shared/src/fs.rs +++ b/shared/src/fs.rs @@ -150,13 +150,39 @@ pub fn file_mode(mode: u32) -> String { s } -// Note: files must contain at least one file path -pub fn in_same_root(files: &[PathBuf]) -> Option { +// Find the max common root of a list of files +// e.g. /a/b/c, /a/b/d -> /a/b +// /aa/bb/cc, /aa/dd/ee -> /aa +pub fn max_common_root(files: &[PathBuf]) -> PathBuf { if files.is_empty() { - return None; + return PathBuf::new(); } - let mut files = files.iter(); - let parent = files.next().map(|f| f.parent().unwrap_or(f).to_path_buf()).unwrap(); - if files.all(|f| f.parent().unwrap_or(f) == parent) { Some(parent) } else { None } + let mut it = files.iter().map(|p| p.components()); + let mut root = it.next().unwrap().collect::(); + for components in it { + let mut new_root = PathBuf::new(); + for (a, b) in root.components().zip(components) { + if a != b { + break; + } + new_root.push(a); + } + root = new_root; + } + root +} + +#[test] +fn test_max_common_root() { + assert_eq!(max_common_root(&[]).as_os_str(), ""); + assert_eq!(max_common_root(&["".into()]).as_os_str(), ""); + assert_eq!(max_common_root(&["/a/b".into()]).as_os_str(), "/a/b"); + assert_eq!(max_common_root(&["/a/b/c".into(), "/a/b/d".into()]).as_os_str(), "/a/b"); + assert_eq!(max_common_root(&["/aa/bb/cc".into(), "/aa/dd/ee".into()]).as_os_str(), "/aa"); + assert_eq!( + max_common_root(&["/aa/bb/cc".into(), "/aa/bb/cc/dd/ee".into(), "/aa/bb/cc/ff".into()]) + .as_os_str(), + "/aa/bb/cc" + ); }