2024-03-20 11:54:37 -06:00
|
|
|
use cosmic::iced::futures::{channel::mpsc, executor, SinkExt};
|
|
|
|
|
use std::{fs, path::PathBuf, sync::Arc};
|
2024-01-29 11:58:50 -07:00
|
|
|
|
2024-02-01 15:14:14 -07:00
|
|
|
use crate::app::Message;
|
2024-01-30 10:47:41 -07:00
|
|
|
|
|
|
|
|
fn err_str<T: ToString>(err: T) -> String {
|
|
|
|
|
err.to_string()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
2024-01-29 11:58:50 -07:00
|
|
|
pub enum Operation {
|
2024-01-30 10:47:41 -07:00
|
|
|
/// Copy items
|
2024-02-27 13:25:50 -07:00
|
|
|
Copy {
|
|
|
|
|
paths: Vec<PathBuf>,
|
|
|
|
|
to: PathBuf,
|
|
|
|
|
},
|
2024-01-30 10:47:41 -07:00
|
|
|
/// Move items to the trash
|
2024-02-27 13:25:50 -07:00
|
|
|
Delete {
|
|
|
|
|
paths: Vec<PathBuf>,
|
|
|
|
|
},
|
2024-05-09 13:24:06 -06:00
|
|
|
/// Empty the trash
|
|
|
|
|
EmptyTrash,
|
2024-01-30 10:47:41 -07:00
|
|
|
/// Move items
|
2024-02-27 13:25:50 -07:00
|
|
|
Move {
|
|
|
|
|
paths: Vec<PathBuf>,
|
|
|
|
|
to: PathBuf,
|
|
|
|
|
},
|
|
|
|
|
NewFile {
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
},
|
|
|
|
|
NewFolder {
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
},
|
2024-02-28 15:07:50 -07:00
|
|
|
Rename {
|
|
|
|
|
from: PathBuf,
|
|
|
|
|
to: PathBuf,
|
|
|
|
|
},
|
2024-01-29 11:58:50 -07:00
|
|
|
/// Restore a path from the trash
|
2024-02-27 13:25:50 -07:00
|
|
|
Restore {
|
|
|
|
|
paths: Vec<trash::TrashItem>,
|
|
|
|
|
},
|
2024-01-29 11:58:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Operation {
|
2024-01-30 10:47:41 -07:00
|
|
|
/// Perform the operation
|
2024-03-20 11:54:37 -06:00
|
|
|
pub async fn perform(
|
|
|
|
|
self,
|
|
|
|
|
id: u64,
|
|
|
|
|
msg_tx: &Arc<tokio::sync::Mutex<mpsc::Sender<Message>>>,
|
|
|
|
|
) -> Result<(), String> {
|
|
|
|
|
let _ = msg_tx
|
|
|
|
|
.lock()
|
|
|
|
|
.await
|
|
|
|
|
.send(Message::PendingProgress(id, 0.0))
|
|
|
|
|
.await;
|
2024-01-29 11:58:50 -07:00
|
|
|
|
2024-01-30 10:47:41 -07:00
|
|
|
//TODO: IF ERROR, RETURN AN Operation THAT CAN UNDO THE CURRENT STATE
|
|
|
|
|
//TODO: SAFELY HANDLE CANCEL
|
2024-01-29 11:58:50 -07:00
|
|
|
match self {
|
2024-03-20 11:54:37 -06:00
|
|
|
Self::Copy { paths, to } => {
|
|
|
|
|
let msg_tx = msg_tx.clone();
|
|
|
|
|
tokio::task::spawn_blocking(move || {
|
|
|
|
|
log::info!("Copy {:?} to {:?}", paths, to);
|
|
|
|
|
let options = fs_extra::dir::CopyOptions::default();
|
|
|
|
|
//TODO: set options as desired
|
|
|
|
|
fs_extra::copy_items_with_progress(&paths, &to, &options, |progress| {
|
|
|
|
|
executor::block_on(async {
|
|
|
|
|
let _ = msg_tx
|
|
|
|
|
.lock()
|
|
|
|
|
.await
|
|
|
|
|
.send(Message::PendingProgress(
|
|
|
|
|
id,
|
|
|
|
|
100.0 * (progress.copied_bytes as f32)
|
|
|
|
|
/ (progress.total_bytes as f32),
|
|
|
|
|
))
|
|
|
|
|
.await;
|
|
|
|
|
});
|
|
|
|
|
//TODO: handle exceptions
|
|
|
|
|
fs_extra::dir::TransitProcessResult::ContinueOrAbort
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(err_str)?
|
|
|
|
|
.map_err(err_str)?;
|
|
|
|
|
}
|
2024-01-30 10:47:41 -07:00
|
|
|
Self::Delete { paths } => {
|
2024-02-27 09:58:22 -07:00
|
|
|
let total = paths.len();
|
2024-01-30 10:47:41 -07:00
|
|
|
let mut count = 0;
|
|
|
|
|
for path in paths {
|
|
|
|
|
tokio::task::spawn_blocking(|| trash::delete(path))
|
|
|
|
|
.await
|
|
|
|
|
.map_err(err_str)?
|
|
|
|
|
.map_err(err_str)?;
|
|
|
|
|
count += 1;
|
2024-02-27 09:58:22 -07:00
|
|
|
let _ = msg_tx
|
2024-03-20 11:54:37 -06:00
|
|
|
.lock()
|
|
|
|
|
.await
|
2024-01-30 10:47:41 -07:00
|
|
|
.send(Message::PendingProgress(
|
|
|
|
|
id,
|
|
|
|
|
100.0 * (count as f32) / (total as f32),
|
|
|
|
|
))
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-09 13:24:06 -06:00
|
|
|
Self::EmptyTrash => {
|
|
|
|
|
#[cfg(any(
|
|
|
|
|
target_os = "windows",
|
|
|
|
|
all(
|
|
|
|
|
unix,
|
|
|
|
|
not(target_os = "macos"),
|
|
|
|
|
not(target_os = "ios"),
|
|
|
|
|
not(target_os = "android")
|
|
|
|
|
)
|
|
|
|
|
))]
|
|
|
|
|
{
|
|
|
|
|
tokio::task::spawn_blocking(|| {
|
|
|
|
|
let items = trash::os_limited::list()?;
|
|
|
|
|
trash::os_limited::purge_all(items)
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(err_str)?
|
|
|
|
|
.map_err(err_str)?;
|
|
|
|
|
}
|
|
|
|
|
let _ = msg_tx
|
|
|
|
|
.lock()
|
|
|
|
|
.await
|
|
|
|
|
.send(Message::PendingProgress(id, 100.0))
|
|
|
|
|
.await;
|
|
|
|
|
}
|
2024-03-20 11:54:37 -06:00
|
|
|
Self::Move { paths, to } => {
|
|
|
|
|
let msg_tx = msg_tx.clone();
|
|
|
|
|
tokio::task::spawn_blocking(move || {
|
|
|
|
|
log::info!("Move {:?} to {:?}", paths, to);
|
|
|
|
|
let options = fs_extra::dir::CopyOptions::default();
|
|
|
|
|
//TODO: set options as desired
|
|
|
|
|
fs_extra::move_items_with_progress(&paths, &to, &options, |progress| {
|
|
|
|
|
executor::block_on(async {
|
|
|
|
|
let _ = msg_tx
|
|
|
|
|
.lock()
|
|
|
|
|
.await
|
|
|
|
|
.send(Message::PendingProgress(
|
|
|
|
|
id,
|
|
|
|
|
100.0 * (progress.copied_bytes as f32)
|
|
|
|
|
/ (progress.total_bytes as f32),
|
|
|
|
|
))
|
|
|
|
|
.await;
|
|
|
|
|
});
|
|
|
|
|
//TODO: handle exceptions
|
|
|
|
|
fs_extra::dir::TransitProcessResult::ContinueOrAbort
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
.await
|
|
|
|
|
.map_err(err_str)?
|
|
|
|
|
.map_err(err_str)?;
|
|
|
|
|
}
|
2024-02-27 13:25:50 -07:00
|
|
|
Self::NewFolder { path } => {
|
|
|
|
|
tokio::task::spawn_blocking(|| fs::create_dir(path))
|
|
|
|
|
.await
|
|
|
|
|
.map_err(err_str)?
|
|
|
|
|
.map_err(err_str)?;
|
2024-03-20 11:54:37 -06:00
|
|
|
let _ = msg_tx
|
|
|
|
|
.lock()
|
|
|
|
|
.await
|
|
|
|
|
.send(Message::PendingProgress(id, 100.0))
|
|
|
|
|
.await;
|
2024-02-27 13:25:50 -07:00
|
|
|
}
|
|
|
|
|
Self::NewFile { path } => {
|
|
|
|
|
tokio::task::spawn_blocking(|| fs::File::create(path))
|
|
|
|
|
.await
|
|
|
|
|
.map_err(err_str)?
|
2024-02-28 15:07:50 -07:00
|
|
|
.map_err(err_str)?;
|
2024-03-20 11:54:37 -06:00
|
|
|
let _ = msg_tx
|
|
|
|
|
.lock()
|
|
|
|
|
.await
|
|
|
|
|
.send(Message::PendingProgress(id, 100.0))
|
|
|
|
|
.await;
|
2024-02-28 15:07:50 -07:00
|
|
|
}
|
|
|
|
|
Self::Rename { from, to } => {
|
|
|
|
|
tokio::task::spawn_blocking(|| fs::rename(from, to))
|
|
|
|
|
.await
|
|
|
|
|
.map_err(err_str)?
|
2024-02-27 13:25:50 -07:00
|
|
|
.map_err(err_str)?;
|
2024-03-20 11:54:37 -06:00
|
|
|
let _ = msg_tx
|
|
|
|
|
.lock()
|
|
|
|
|
.await
|
|
|
|
|
.send(Message::PendingProgress(id, 100.0))
|
|
|
|
|
.await;
|
2024-02-27 13:25:50 -07:00
|
|
|
}
|
2024-01-30 10:47:41 -07:00
|
|
|
Self::Restore { paths } => {
|
2024-02-27 09:58:22 -07:00
|
|
|
let total = paths.len();
|
2024-01-30 10:47:41 -07:00
|
|
|
let mut count = 0;
|
|
|
|
|
for path in paths {
|
|
|
|
|
tokio::task::spawn_blocking(|| trash::os_limited::restore_all([path]))
|
|
|
|
|
.await
|
|
|
|
|
.map_err(err_str)?
|
|
|
|
|
.map_err(err_str)?;
|
|
|
|
|
count += 1;
|
2024-02-27 09:58:22 -07:00
|
|
|
let _ = msg_tx
|
2024-03-20 11:54:37 -06:00
|
|
|
.lock()
|
|
|
|
|
.await
|
2024-01-30 10:47:41 -07:00
|
|
|
.send(Message::PendingProgress(
|
|
|
|
|
id,
|
|
|
|
|
100.0 * (count as f32) / (total as f32),
|
|
|
|
|
))
|
|
|
|
|
.await;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-29 11:58:50 -07:00
|
|
|
}
|
|
|
|
|
|
2024-03-20 11:54:37 -06:00
|
|
|
let _ = msg_tx
|
|
|
|
|
.lock()
|
|
|
|
|
.await
|
|
|
|
|
.send(Message::PendingProgress(id, 100.0))
|
|
|
|
|
.await;
|
2024-01-29 11:58:50 -07:00
|
|
|
|
2024-01-30 10:47:41 -07:00
|
|
|
Ok(())
|
2024-01-29 11:58:50 -07:00
|
|
|
}
|
|
|
|
|
}
|