fix: panic when a second zip extract dialog is opened
* Syncs extracted zip files on the compio executor's thread * Close the first zip extract dialog before opening a new one to fix a panic --------- Co-authored-by: James A DellaMorte <dellamorte.james@comcast.net>
This commit is contained in:
parent
c653b69c75
commit
cd427c746c
3 changed files with 49 additions and 23 deletions
10
src/app.rs
10
src/app.rs
|
|
@ -1009,6 +1009,12 @@ impl App {
|
||||||
.and_then(|first| first.as_ref().parent())
|
.and_then(|first| first.as_ref().parent())
|
||||||
.map(Path::to_path_buf)
|
.map(Path::to_path_buf)
|
||||||
{
|
{
|
||||||
|
let mut tasks = Vec::new();
|
||||||
|
if let Some(old_dialog) = self.file_dialog_opt.take() {
|
||||||
|
let old_id = old_dialog.window_id();
|
||||||
|
self.windows.remove(&old_id);
|
||||||
|
tasks.push(window::close(old_id));
|
||||||
|
}
|
||||||
let (mut dialog, dialog_task) = Dialog::new(
|
let (mut dialog, dialog_task) = Dialog::new(
|
||||||
DialogSettings::new()
|
DialogSettings::new()
|
||||||
.kind(DialogKind::OpenFolder)
|
.kind(DialogKind::OpenFolder)
|
||||||
|
|
@ -1025,7 +1031,9 @@ impl App {
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
self.file_dialog_opt = Some(dialog);
|
self.file_dialog_opt = Some(dialog);
|
||||||
Task::batch([set_title_task, dialog_task])
|
tasks.push(set_title_task);
|
||||||
|
tasks.push(dialog_task);
|
||||||
|
Task::batch(tasks)
|
||||||
} else {
|
} else {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::mime_icon::mime_for_path;
|
use crate::mime_icon::mime_for_path;
|
||||||
use crate::operation::{Controller, OpReader, OperationError, OperationErrorType, sync_to_disk};
|
use crate::operation::{Controller, OpReader, OperationError, OperationErrorType};
|
||||||
use cosmic::iced::futures;
|
use cosmic::iced::futures;
|
||||||
use jiff::Zoned;
|
use jiff::Zoned;
|
||||||
use jiff::civil::DateTime;
|
use jiff::civil::DateTime;
|
||||||
|
|
@ -45,7 +45,7 @@ pub fn extract(
|
||||||
new_dir: &Path,
|
new_dir: &Path,
|
||||||
password: &Option<String>,
|
password: &Option<String>,
|
||||||
controller: &Controller,
|
controller: &Controller,
|
||||||
) -> Result<(), OperationError> {
|
) -> Result<(Vec<PathBuf>, HashSet<PathBuf>), OperationError> {
|
||||||
let mime = mime_for_path(path, None, false);
|
let mime = mime_for_path(path, None, false);
|
||||||
let password = password.as_deref();
|
let password = password.as_deref();
|
||||||
match mime.essence_str() {
|
match mime.essence_str() {
|
||||||
|
|
@ -55,13 +55,15 @@ pub fn extract(
|
||||||
.map(flate2::read::GzDecoder::new)
|
.map(flate2::read::GzDecoder::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(|e| OperationError::from_err(e, controller))?;
|
.map_err(|e| OperationError::from_err(e, controller))
|
||||||
|
.map(|_| Default::default())
|
||||||
}
|
}
|
||||||
"application/x-tar" => OpReader::new(path, controller.clone())
|
"application/x-tar" => OpReader::new(path, controller.clone())
|
||||||
.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(|e| OperationError::from_err(e, controller))?,
|
.map_err(|e| OperationError::from_err(e, controller))
|
||||||
|
.map(|_| Default::default()),
|
||||||
"application/zip" => fs::File::open(path)
|
"application/zip" => fs::File::open(path)
|
||||||
.map(io::BufReader::new)
|
.map(io::BufReader::new)
|
||||||
.map(zip::ZipArchive::new)
|
.map(zip::ZipArchive::new)
|
||||||
|
|
@ -75,7 +77,7 @@ pub fn extract(
|
||||||
OperationError::from_kind(OperationErrorType::PasswordRequired, controller)
|
OperationError::from_kind(OperationErrorType::PasswordRequired, controller)
|
||||||
}
|
}
|
||||||
_ => OperationError::from_err(e, controller),
|
_ => OperationError::from_err(e, controller),
|
||||||
})?,
|
}),
|
||||||
#[cfg(feature = "bzip2")]
|
#[cfg(feature = "bzip2")]
|
||||||
"application/x-bzip"
|
"application/x-bzip"
|
||||||
| "application/x-bzip-compressed-tar"
|
| "application/x-bzip-compressed-tar"
|
||||||
|
|
@ -85,7 +87,8 @@ pub fn extract(
|
||||||
.map(bzip2::read::BzDecoder::new)
|
.map(bzip2::read::BzDecoder::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(|e| OperationError::from_err(e, controller))?,
|
.map_err(|e| OperationError::from_err(e, controller))
|
||||||
|
.map(|_| Default::default()),
|
||||||
#[cfg(feature = "lzma-rust2")]
|
#[cfg(feature = "lzma-rust2")]
|
||||||
"application/x-xz" | "application/x-xz-compressed-tar" => {
|
"application/x-xz" | "application/x-xz-compressed-tar" => {
|
||||||
OpReader::new(path, controller.clone())
|
OpReader::new(path, controller.clone())
|
||||||
|
|
@ -93,14 +96,14 @@ pub fn extract(
|
||||||
.map(|reader| lzma_rust2::XzReader::new(reader, true))
|
.map(|reader| lzma_rust2::XzReader::new(reader, true))
|
||||||
.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(|e| OperationError::from_err(e, controller))?;
|
.map_err(|e| OperationError::from_err(e, controller))
|
||||||
|
.map(|_| Default::default())
|
||||||
}
|
}
|
||||||
_ => Err(OperationError::from_err(
|
_ => Err(OperationError::from_err(
|
||||||
format!("unsupported mime type {mime:?}"),
|
format!("unsupported mime type {mime:?}"),
|
||||||
controller,
|
controller,
|
||||||
))?,
|
)),
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// From https://docs.rs/zip/latest/zip/read/struct.ZipArchive.html#method.extract, with cancellation and progress added
|
// From https://docs.rs/zip/latest/zip/read/struct.ZipArchive.html#method.extract, with cancellation and progress added
|
||||||
|
|
@ -109,7 +112,7 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
||||||
directory: P,
|
directory: P,
|
||||||
password: Option<&str>,
|
password: Option<&str>,
|
||||||
controller: Controller,
|
controller: Controller,
|
||||||
) -> zip::result::ZipResult<()> {
|
) -> zip::result::ZipResult<(Vec<PathBuf>, HashSet<PathBuf>)> {
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use zip::result::ZipError;
|
use zip::result::ZipError;
|
||||||
|
|
@ -282,10 +285,7 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flush files to disk
|
Ok((written_files, target_dirs))
|
||||||
futures::executor::block_on(async { sync_to_disk(written_files, target_dirs).await });
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn zip_date_time_to_system_time(date_time: zip::DateTime) -> Option<SystemTime> {
|
fn zip_date_time_to_system_time(date_time: zip::DateTime) -> Option<SystemTime> {
|
||||||
|
|
|
||||||
|
|
@ -967,11 +967,13 @@ impl Operation {
|
||||||
password,
|
password,
|
||||||
} => {
|
} => {
|
||||||
let controller_clone = controller.clone();
|
let controller_clone = controller.clone();
|
||||||
compio::runtime::spawn_blocking(
|
compio::runtime::spawn(async move {
|
||||||
move || -> Result<OperationSelection, OperationError> {
|
let extracted = compio::runtime::spawn_blocking(move || {
|
||||||
let controller = controller_clone;
|
let controller = controller_clone;
|
||||||
let total_paths = paths.len();
|
let total_paths = paths.len();
|
||||||
let mut op_sel = OperationSelection::default();
|
let mut op_sel = OperationSelection::default();
|
||||||
|
let mut written_files = Vec::new();
|
||||||
|
let mut target_dirs = std::collections::HashSet::new();
|
||||||
for (i, path) in paths.iter().enumerate() {
|
for (i, path) in paths.iter().enumerate() {
|
||||||
futures::executor::block_on(async {
|
futures::executor::block_on(async {
|
||||||
controller
|
controller
|
||||||
|
|
@ -995,16 +997,32 @@ impl Operation {
|
||||||
op_sel.ignored.push(path.clone());
|
op_sel.ignored.push(path.clone());
|
||||||
op_sel.selected.push(new_dir.clone());
|
op_sel.selected.push(new_dir.clone());
|
||||||
|
|
||||||
crate::archive::extract(path, &new_dir, &password, &controller)?;
|
let (files, dirs) = crate::archive::extract(
|
||||||
|
path,
|
||||||
|
&new_dir,
|
||||||
|
&password,
|
||||||
|
&controller,
|
||||||
|
)?;
|
||||||
|
written_files.extend(files);
|
||||||
|
target_dirs.extend(dirs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(op_sel)
|
Ok::<_, OperationError>((op_sel, written_files, target_dirs))
|
||||||
},
|
})
|
||||||
)
|
.await
|
||||||
|
.map_err(wrap_compio_spawn_error)??;
|
||||||
|
|
||||||
|
let (op_sel, written_files, target_dirs) = extracted;
|
||||||
|
if !written_files.is_empty() || !target_dirs.is_empty() {
|
||||||
|
sync_to_disk(written_files, target_dirs).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok::<_, OperationError>(op_sel)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(wrap_compio_spawn_error)?
|
||||||
}
|
}
|
||||||
.await
|
|
||||||
.map_err(wrap_compio_spawn_error)?,
|
|
||||||
Self::Move {
|
Self::Move {
|
||||||
paths,
|
paths,
|
||||||
to,
|
to,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue