Add Compress menu option to add files to zip archive
This commit is contained in:
parent
a85d51acac
commit
13cd7138cc
6 changed files with 268 additions and 29 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1271,6 +1271,7 @@ dependencies = [
|
||||||
"url",
|
"url",
|
||||||
"uzers",
|
"uzers",
|
||||||
"vergen",
|
"vergen",
|
||||||
|
"walkdir",
|
||||||
"xdg",
|
"xdg",
|
||||||
"xdg-mime",
|
"xdg-mime",
|
||||||
"zip",
|
"zip",
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ tar = "0.4.41"
|
||||||
tokio = { version = "1", features = ["sync"] }
|
tokio = { version = "1", features = ["sync"] }
|
||||||
trash = { git = "https://github.com/jackpot51/trash-rs.git", branch = "delete-info" }
|
trash = { git = "https://github.com/jackpot51/trash-rs.git", branch = "delete-info" }
|
||||||
url = "2.5"
|
url = "2.5"
|
||||||
|
walkdir = "2.5.0"
|
||||||
xdg = { version = "2.5.2", optional = true }
|
xdg = { version = "2.5.2", optional = true }
|
||||||
xdg-mime = "0.3"
|
xdg-mime = "0.3"
|
||||||
# Internationalization
|
# Internationalization
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ size = Size
|
||||||
|
|
||||||
# Dialogs
|
# Dialogs
|
||||||
|
|
||||||
|
## Compress Dialog
|
||||||
|
create-archive = Create archive
|
||||||
|
|
||||||
## Empty Trash Dialog
|
## Empty Trash Dialog
|
||||||
empty-trash = Empty trash
|
empty-trash = Empty trash
|
||||||
empty-trash-warning = Are you sure you want to permanently delete all the items in Trash?
|
empty-trash-warning = Are you sure you want to permanently delete all the items in Trash?
|
||||||
|
|
@ -32,6 +35,7 @@ name-no-slashes = Name cannot contain slashes.
|
||||||
|
|
||||||
## Open/Save Dialog
|
## Open/Save Dialog
|
||||||
cancel = Cancel
|
cancel = Cancel
|
||||||
|
create = Create
|
||||||
open = Open
|
open = Open
|
||||||
open-file = Open file
|
open-file = Open file
|
||||||
open-folder = Open folder
|
open-folder = Open folder
|
||||||
|
|
@ -79,6 +83,14 @@ no-history = No items in history.
|
||||||
pending = Pending
|
pending = Pending
|
||||||
failed = Failed
|
failed = Failed
|
||||||
complete = Complete
|
complete = Complete
|
||||||
|
compressing = Compressing {$items} {$items ->
|
||||||
|
[one] item
|
||||||
|
*[other] items
|
||||||
|
} from {$from} to {$to}
|
||||||
|
compressed = Compressed {$items} {$items ->
|
||||||
|
[one] item
|
||||||
|
*[other] items
|
||||||
|
} from {$from} to {$to}
|
||||||
copy_noun = Copy
|
copy_noun = Copy
|
||||||
creating = Creating {$name} in {$parent}
|
creating = Creating {$name} in {$parent}
|
||||||
created = Created {$name} in {$parent}
|
created = Created {$name} in {$parent}
|
||||||
|
|
@ -148,8 +160,9 @@ dark = Dark
|
||||||
light = Light
|
light = Light
|
||||||
|
|
||||||
# Context menu
|
# Context menu
|
||||||
extract-here = Extract
|
|
||||||
add-to-sidebar = Add to sidebar
|
add-to-sidebar = Add to sidebar
|
||||||
|
compress = Compress
|
||||||
|
extract-here = Extract
|
||||||
new-file = New file...
|
new-file = New file...
|
||||||
new-folder = New folder...
|
new-folder = New folder...
|
||||||
open-in-terminal = Open in terminal
|
open-in-terminal = Open in terminal
|
||||||
|
|
|
||||||
122
src/app.rs
122
src/app.rs
|
|
@ -67,6 +67,7 @@ pub struct Flags {
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
About,
|
About,
|
||||||
AddToSidebar,
|
AddToSidebar,
|
||||||
|
Compress,
|
||||||
Copy,
|
Copy,
|
||||||
Cut,
|
Cut,
|
||||||
EditHistory,
|
EditHistory,
|
||||||
|
|
@ -116,6 +117,7 @@ impl Action {
|
||||||
match self {
|
match self {
|
||||||
Action::About => Message::ToggleContextPage(ContextPage::About),
|
Action::About => Message::ToggleContextPage(ContextPage::About),
|
||||||
Action::AddToSidebar => Message::AddToSidebar(entity_opt),
|
Action::AddToSidebar => Message::AddToSidebar(entity_opt),
|
||||||
|
Action::Compress => Message::Compress(entity_opt),
|
||||||
Action::Copy => Message::Copy(entity_opt),
|
Action::Copy => Message::Copy(entity_opt),
|
||||||
Action::Cut => Message::Cut(entity_opt),
|
Action::Cut => Message::Cut(entity_opt),
|
||||||
Action::EditHistory => Message::ToggleContextPage(ContextPage::EditHistory),
|
Action::EditHistory => Message::ToggleContextPage(ContextPage::EditHistory),
|
||||||
|
|
@ -210,6 +212,7 @@ pub enum Message {
|
||||||
AddToSidebar(Option<Entity>),
|
AddToSidebar(Option<Entity>),
|
||||||
AppTheme(AppTheme),
|
AppTheme(AppTheme),
|
||||||
CloseToast(widget::ToastId),
|
CloseToast(widget::ToastId),
|
||||||
|
Compress(Option<Entity>),
|
||||||
Config(Config),
|
Config(Config),
|
||||||
Copy(Option<Entity>),
|
Copy(Option<Entity>),
|
||||||
Cut(Option<Entity>),
|
Cut(Option<Entity>),
|
||||||
|
|
@ -297,8 +300,27 @@ impl ContextPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
|
pub enum ArchiveType {
|
||||||
|
Zip,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArchiveType {
|
||||||
|
pub fn extension(&self) -> String {
|
||||||
|
match self {
|
||||||
|
ArchiveType::Zip => ".zip".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum DialogPage {
|
pub enum DialogPage {
|
||||||
|
Compress {
|
||||||
|
paths: Vec<PathBuf>,
|
||||||
|
to: PathBuf,
|
||||||
|
name: String,
|
||||||
|
archive_type: ArchiveType,
|
||||||
|
},
|
||||||
EmptyTrash,
|
EmptyTrash,
|
||||||
FailedOperation(u64),
|
FailedOperation(u64),
|
||||||
NewItem {
|
NewItem {
|
||||||
|
|
@ -1264,6 +1286,23 @@ impl Application for App {
|
||||||
config_set!(app_theme, app_theme);
|
config_set!(app_theme, app_theme);
|
||||||
return self.update_config();
|
return self.update_config();
|
||||||
}
|
}
|
||||||
|
Message::Compress(entity_opt) => {
|
||||||
|
let paths = self.selected_paths(entity_opt);
|
||||||
|
if let Some(current_path) = paths.first() {
|
||||||
|
if let Some(destination) = current_path.parent().zip(current_path.file_stem()) {
|
||||||
|
let to = destination.0.to_path_buf();
|
||||||
|
let name = destination.1.to_str().unwrap_or_default().to_string();
|
||||||
|
let archive_type = ArchiveType::Zip;
|
||||||
|
self.dialog_pages.push_back(DialogPage::Compress {
|
||||||
|
paths,
|
||||||
|
to,
|
||||||
|
name,
|
||||||
|
archive_type,
|
||||||
|
});
|
||||||
|
return widget::text_input::focus(self.dialog_text_input.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Message::Config(config) => {
|
Message::Config(config) => {
|
||||||
if config != self.config {
|
if config != self.config {
|
||||||
log::info!("update config");
|
log::info!("update config");
|
||||||
|
|
@ -1291,6 +1330,21 @@ impl Application for App {
|
||||||
Message::DialogComplete => {
|
Message::DialogComplete => {
|
||||||
if let Some(dialog_page) = self.dialog_pages.pop_front() {
|
if let Some(dialog_page) = self.dialog_pages.pop_front() {
|
||||||
match dialog_page {
|
match dialog_page {
|
||||||
|
DialogPage::Compress {
|
||||||
|
paths,
|
||||||
|
to,
|
||||||
|
name,
|
||||||
|
archive_type,
|
||||||
|
} => {
|
||||||
|
let extension = archive_type.extension();
|
||||||
|
let name = format!("{}{}", name, extension);
|
||||||
|
let to = to.join(name);
|
||||||
|
self.operation(Operation::Compress {
|
||||||
|
paths,
|
||||||
|
to,
|
||||||
|
archive_type,
|
||||||
|
})
|
||||||
|
}
|
||||||
DialogPage::EmptyTrash => {
|
DialogPage::EmptyTrash => {
|
||||||
self.operation(Operation::EmptyTrash);
|
self.operation(Operation::EmptyTrash);
|
||||||
}
|
}
|
||||||
|
|
@ -2323,6 +2377,74 @@ impl Application for App {
|
||||||
let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing;
|
let cosmic_theme::Spacing { space_xxs, .. } = theme::active().cosmic().spacing;
|
||||||
|
|
||||||
let dialog = match dialog_page {
|
let dialog = match dialog_page {
|
||||||
|
DialogPage::Compress {
|
||||||
|
paths,
|
||||||
|
to,
|
||||||
|
name,
|
||||||
|
archive_type,
|
||||||
|
} => {
|
||||||
|
let mut dialog = widget::dialog(fl!("create-archive"));
|
||||||
|
|
||||||
|
let complete_maybe = if name.is_empty() {
|
||||||
|
None
|
||||||
|
} else if name == "." || name == ".." {
|
||||||
|
dialog = dialog.tertiary_action(widget::text::body(fl!(
|
||||||
|
"name-invalid",
|
||||||
|
filename = name.as_str()
|
||||||
|
)));
|
||||||
|
None
|
||||||
|
} else if name.contains('/') {
|
||||||
|
dialog = dialog.tertiary_action(widget::text::body(fl!("name-no-slashes")));
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let extension = archive_type.extension();
|
||||||
|
let name = format!("{}{}", name, extension);
|
||||||
|
let path = to.join(&name);
|
||||||
|
if path.exists() {
|
||||||
|
dialog =
|
||||||
|
dialog.tertiary_action(widget::text::body(fl!("file-already-exists")));
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
if name.starts_with('.') {
|
||||||
|
dialog = dialog.tertiary_action(widget::text::body(fl!("name-hidden")));
|
||||||
|
}
|
||||||
|
Some(Message::DialogComplete)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
dialog
|
||||||
|
.primary_action(
|
||||||
|
widget::button::suggested(fl!("create"))
|
||||||
|
.on_press_maybe(complete_maybe.clone()),
|
||||||
|
)
|
||||||
|
.secondary_action(
|
||||||
|
widget::button::standard(fl!("cancel")).on_press(Message::DialogCancel),
|
||||||
|
)
|
||||||
|
.control(
|
||||||
|
widget::column::with_children(vec![
|
||||||
|
widget::text::body(fl!("file-name")).into(),
|
||||||
|
widget::row::with_children(vec![
|
||||||
|
widget::text_input("", name.as_str())
|
||||||
|
.id(self.dialog_text_input.clone())
|
||||||
|
.on_input(move |name| {
|
||||||
|
Message::DialogUpdate(DialogPage::Compress {
|
||||||
|
paths: paths.clone(),
|
||||||
|
to: to.clone(),
|
||||||
|
name: name.clone(),
|
||||||
|
archive_type: archive_type.clone(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on_submit_maybe(complete_maybe)
|
||||||
|
.into(),
|
||||||
|
widget::text::body(".zip").into(),
|
||||||
|
])
|
||||||
|
.align_items(Alignment::Center)
|
||||||
|
.spacing(space_xxs)
|
||||||
|
.into(),
|
||||||
|
])
|
||||||
|
.spacing(space_xxs),
|
||||||
|
)
|
||||||
|
}
|
||||||
DialogPage::EmptyTrash => widget::dialog(fl!("empty-trash"))
|
DialogPage::EmptyTrash => widget::dialog(fl!("empty-trash"))
|
||||||
.body(fl!("empty-trash-warning"))
|
.body(fl!("empty-trash-warning"))
|
||||||
.primary_action(
|
.primary_action(
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,7 @@ pub fn context_menu<'a>(
|
||||||
children.push(menu_item(fl!("cut"), Action::Cut).into());
|
children.push(menu_item(fl!("cut"), Action::Cut).into());
|
||||||
children.push(menu_item(fl!("copy"), Action::Copy).into());
|
children.push(menu_item(fl!("copy"), Action::Copy).into());
|
||||||
|
|
||||||
|
children.push(container(horizontal_rule(1)).padding([0, 8]).into());
|
||||||
let supported_archive_types = ["application/x-tar", "application/zip"]
|
let supported_archive_types = ["application/x-tar", "application/zip"]
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|mime_type| mime_type.parse::<Mime>().ok())
|
.filter_map(|mime_type| mime_type.parse::<Mime>().ok())
|
||||||
|
|
@ -133,6 +134,8 @@ pub fn context_menu<'a>(
|
||||||
if selected_types.is_empty() {
|
if selected_types.is_empty() {
|
||||||
children.push(menu_item(fl!("extract-here"), Action::ExtractHere).into());
|
children.push(menu_item(fl!("extract-here"), Action::ExtractHere).into());
|
||||||
}
|
}
|
||||||
|
children.push(menu_item(fl!("compress"), Action::Compress).into());
|
||||||
|
children.push(container(horizontal_rule(1)).padding([0, 8]).into());
|
||||||
|
|
||||||
//TODO: Print?
|
//TODO: Print?
|
||||||
children.push(container(horizontal_rule(1)).padding([0, 8]).into());
|
children.push(container(horizontal_rule(1)).padding([0, 8]).into());
|
||||||
|
|
|
||||||
155
src/operation.rs
155
src/operation.rs
|
|
@ -1,20 +1,19 @@
|
||||||
use cosmic::iced::futures::{channel::mpsc::Sender, executor, SinkExt};
|
use cosmic::iced::futures::{channel::mpsc::Sender, executor, SinkExt};
|
||||||
use mime_guess::MimeGuess;
|
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
fs,
|
fs,
|
||||||
io::{self, Error},
|
io::{self, Read, Write},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
process,
|
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{self, AtomicU64},
|
atomic::{self, AtomicU64},
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{mpsc, Mutex};
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{DialogPage, Message},
|
app::{ArchiveType, DialogPage, Message},
|
||||||
config::IconSizes,
|
config::IconSizes,
|
||||||
fl, tab,
|
fl, tab,
|
||||||
};
|
};
|
||||||
|
|
@ -130,6 +129,12 @@ impl From<ReplaceResult> for fs_extra::dir::TransitProcessResult {
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
|
/// Compress files
|
||||||
|
Compress {
|
||||||
|
paths: Vec<PathBuf>,
|
||||||
|
to: PathBuf,
|
||||||
|
archive_type: ArchiveType,
|
||||||
|
},
|
||||||
/// Copy items
|
/// Copy items
|
||||||
Copy {
|
Copy {
|
||||||
paths: Vec<PathBuf>,
|
paths: Vec<PathBuf>,
|
||||||
|
|
@ -241,6 +246,12 @@ fn paths_parent_name<'a>(paths: &'a Vec<PathBuf>) -> Cow<'a, str> {
|
||||||
impl Operation {
|
impl Operation {
|
||||||
pub fn pending_text(&self) -> String {
|
pub fn pending_text(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Compress { paths, to, .. } => fl!(
|
||||||
|
"compressing",
|
||||||
|
items = paths.len(),
|
||||||
|
from = paths_parent_name(paths),
|
||||||
|
to = file_name(to)
|
||||||
|
),
|
||||||
Self::Copy { paths, to } => fl!(
|
Self::Copy { paths, to } => fl!(
|
||||||
"copying",
|
"copying",
|
||||||
items = paths.len(),
|
items = paths.len(),
|
||||||
|
|
@ -285,6 +296,12 @@ impl Operation {
|
||||||
|
|
||||||
pub fn completed_text(&self) -> String {
|
pub fn completed_text(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Compress { paths, to, .. } => fl!(
|
||||||
|
"compressed",
|
||||||
|
items = paths.len(),
|
||||||
|
from = paths_parent_name(paths),
|
||||||
|
to = file_name(to)
|
||||||
|
),
|
||||||
Self::Copy { paths, to } => fl!(
|
Self::Copy { paths, to } => fl!(
|
||||||
"copied",
|
"copied",
|
||||||
items = paths.len(),
|
items = paths.len(),
|
||||||
|
|
@ -327,7 +344,9 @@ impl Operation {
|
||||||
|
|
||||||
pub fn toast(&self) -> Option<String> {
|
pub fn toast(&self) -> Option<String> {
|
||||||
match self {
|
match self {
|
||||||
|
Self::Compress { .. } => Some(self.completed_text()),
|
||||||
Self::Delete { .. } => Some(self.completed_text()),
|
Self::Delete { .. } => Some(self.completed_text()),
|
||||||
|
Self::Extract { .. } => Some(self.completed_text()),
|
||||||
//TODO: more toasts
|
//TODO: more toasts
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
@ -348,6 +367,79 @@ impl Operation {
|
||||||
//TODO: IF ERROR, RETURN AN Operation THAT CAN UNDO THE CURRENT STATE
|
//TODO: IF ERROR, RETURN AN Operation THAT CAN UNDO THE CURRENT STATE
|
||||||
//TODO: SAFELY HANDLE CANCEL
|
//TODO: SAFELY HANDLE CANCEL
|
||||||
match self {
|
match self {
|
||||||
|
Self::Compress {
|
||||||
|
paths,
|
||||||
|
to,
|
||||||
|
archive_type,
|
||||||
|
} => {
|
||||||
|
let msg_tx = msg_tx.clone();
|
||||||
|
tokio::task::spawn_blocking(move || -> Result<(), String> {
|
||||||
|
match archive_type {
|
||||||
|
ArchiveType::Zip => {
|
||||||
|
let mut archive = fs::File::create(&to)
|
||||||
|
.map(io::BufWriter::new)
|
||||||
|
.map(zip::ZipWriter::new)
|
||||||
|
.map_err(err_str)?;
|
||||||
|
|
||||||
|
let zip_options = zip::write::SimpleFileOptions::default();
|
||||||
|
|
||||||
|
let mut paths = paths;
|
||||||
|
for path in paths.clone().iter() {
|
||||||
|
if path.is_dir() {
|
||||||
|
let new_paths_it = WalkDir::new(path).into_iter();
|
||||||
|
for entry in new_paths_it.skip(1) {
|
||||||
|
let entry = entry.map_err(err_str)?;
|
||||||
|
paths.push(entry.path().to_path_buf());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let total_paths = paths.len();
|
||||||
|
for (i, path) in paths.iter().enumerate() {
|
||||||
|
executor::block_on(async {
|
||||||
|
let total_progress = (i as f32) / total_paths as f32;
|
||||||
|
let _ = msg_tx
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.send(Message::PendingProgress(id, 100.0 * total_progress))
|
||||||
|
.await;
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(relative_root) = to.parent() {
|
||||||
|
if let Some(relative_path) =
|
||||||
|
path.strip_prefix(relative_root).map_err(err_str)?.to_str()
|
||||||
|
{
|
||||||
|
if path.is_file() {
|
||||||
|
archive
|
||||||
|
.start_file(relative_path, zip_options)
|
||||||
|
.map_err(err_str)?;
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
let mut file = fs::File::open(&path)
|
||||||
|
.map(io::BufReader::new)
|
||||||
|
.map_err(err_str)?;
|
||||||
|
|
||||||
|
file.read_to_end(&mut buffer).map_err(err_str)?;
|
||||||
|
archive.write_all(&buffer).map_err(err_str)?;
|
||||||
|
} else {
|
||||||
|
archive
|
||||||
|
.add_directory(relative_path, zip_options)
|
||||||
|
.map_err(err_str)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
archive.finish().map_err(err_str)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(err_str)?
|
||||||
|
.map_err(err_str)?;
|
||||||
|
}
|
||||||
Self::Copy { paths, to } => {
|
Self::Copy { paths, to } => {
|
||||||
// Handle duplicate file names by renaming paths
|
// Handle duplicate file names by renaming paths
|
||||||
let (paths, to): (Vec<_>, Vec<_>) = tokio::task::spawn_blocking(move || {
|
let (paths, to): (Vec<_>, Vec<_>) = tokio::task::spawn_blocking(move || {
|
||||||
|
|
@ -494,10 +586,21 @@ impl Operation {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
Self::Extract { paths, to } => {
|
Self::Extract { paths, to } => {
|
||||||
for path in paths {
|
let msg_tx = msg_tx.clone();
|
||||||
let to = to.to_owned();
|
tokio::task::spawn_blocking(move || -> Result<(), String> {
|
||||||
|
let total_paths = paths.len();
|
||||||
|
for (i, path) in paths.iter().enumerate() {
|
||||||
|
executor::block_on(async {
|
||||||
|
let total_progress = (i as f32) / total_paths as f32;
|
||||||
|
let _ = msg_tx
|
||||||
|
.lock()
|
||||||
|
.await
|
||||||
|
.send(Message::PendingProgress(id, 100.0 * total_progress))
|
||||||
|
.await;
|
||||||
|
});
|
||||||
|
|
||||||
|
let to = to.to_owned();
|
||||||
|
|
||||||
tokio::task::spawn_blocking(move || -> Result<(), String> {
|
|
||||||
if let Some(file_stem) = path.file_stem() {
|
if let Some(file_stem) = path.file_stem() {
|
||||||
let mut new_dir = to.join(file_stem);
|
let mut new_dir = to.join(file_stem);
|
||||||
if new_dir.exists() {
|
if new_dir.exists() {
|
||||||
|
|
@ -510,32 +613,28 @@ impl Operation {
|
||||||
|
|
||||||
if let Some(mime) = mime_guess::from_path(&path).first() {
|
if let Some(mime) = mime_guess::from_path(&path).first() {
|
||||||
match mime.essence_str() {
|
match mime.essence_str() {
|
||||||
"application/x-tar" => {
|
"application/x-tar" => fs::File::open(path)
|
||||||
return fs::File::open(path)
|
.map(io::BufReader::new)
|
||||||
.map(io::BufReader::new)
|
.map(tar::Archive::new)
|
||||||
.map(tar::Archive::new)
|
.and_then(|mut archive| archive.unpack(new_dir))
|
||||||
.and_then(|mut archive| archive.unpack(new_dir))
|
.map_err(err_str)?,
|
||||||
.map_err(err_str)
|
"application/zip" => fs::File::open(path)
|
||||||
}
|
.map(io::BufReader::new)
|
||||||
"application/zip" => {
|
.map(zip::ZipArchive::new)
|
||||||
return fs::File::open(path)
|
.map_err(err_str)?
|
||||||
.map(io::BufReader::new)
|
.and_then(|mut archive| archive.extract(new_dir))
|
||||||
.map(zip::ZipArchive::new)
|
.map_err(err_str)?,
|
||||||
.map_err(err_str)?
|
|
||||||
.and_then(|mut archive| archive.extract(new_dir))
|
|
||||||
.map_err(err_str)
|
|
||||||
}
|
|
||||||
_ => Err(format!("unsupported mime type {:?}", mime))?,
|
_ => Err(format!("unsupported mime type {:?}", mime))?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(err_str)?
|
.map_err(err_str)?
|
||||||
.map_err(err_str)?;
|
.map_err(err_str)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Self::Move { paths, to } => {
|
Self::Move { paths, to } => {
|
||||||
let msg_tx = msg_tx.clone();
|
let msg_tx = msg_tx.clone();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue