Format
This commit is contained in:
parent
9e2aa3433a
commit
22bca8632b
2 changed files with 300 additions and 256 deletions
|
|
@ -1,17 +1,3 @@
|
|||
use cosmic::iced::futures::{channel::mpsc::Sender, executor, SinkExt};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fs,
|
||||
io::{self, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::{Formatter};
|
||||
use tokio::sync::{mpsc, Mutex as TokioMutex};
|
||||
use walkdir::WalkDir;
|
||||
use zip::AesMode::Aes256;
|
||||
use zip::result::ZipError;
|
||||
use crate::{
|
||||
app::{ArchiveType, DialogPage, Message},
|
||||
config::IconSizes,
|
||||
|
|
@ -20,6 +6,20 @@ use crate::{
|
|||
spawn_detached::spawn_detached,
|
||||
tab,
|
||||
};
|
||||
use cosmic::iced::futures::{channel::mpsc::Sender, executor, SinkExt};
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::Formatter;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fs,
|
||||
io::{self, Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::sync::{mpsc, Mutex as TokioMutex};
|
||||
use walkdir::WalkDir;
|
||||
use zip::result::ZipError;
|
||||
use zip::AesMode::Aes256;
|
||||
|
||||
pub use self::controller::{Controller, ControllerState};
|
||||
pub mod controller;
|
||||
|
|
@ -94,7 +94,7 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
|||
archive: &mut zip::ZipArchive<R>,
|
||||
directory: P,
|
||||
controller: Controller,
|
||||
password: Option<String>
|
||||
password: Option<String>,
|
||||
) -> zip::result::ZipResult<()> {
|
||||
use std::{ffi::OsString, fs};
|
||||
use zip::result::ZipError;
|
||||
|
|
@ -115,7 +115,6 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[cfg(unix)]
|
||||
let mut files_by_unix_mode = Vec::new();
|
||||
let mut buffer = vec![0; 4 * 1024 * 1024];
|
||||
|
|
@ -131,8 +130,9 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
|||
|
||||
let mut file = match &password {
|
||||
None => archive.by_index(i),
|
||||
Some(pwd) => archive.by_index_decrypt(i, pwd.as_bytes())
|
||||
}.map_err(|e| e)?;
|
||||
Some(pwd) => archive.by_index_decrypt(i, pwd.as_bytes()),
|
||||
}
|
||||
.map_err(|e| e)?;
|
||||
let filepath = file
|
||||
.enclosed_name()
|
||||
.ok_or(ZipError::InvalidArchive("Invalid file path"))?;
|
||||
|
|
@ -193,8 +193,9 @@ fn zip_extract<R: io::Read + io::Seek, P: AsRef<Path>>(
|
|||
}
|
||||
let mut file = match &password {
|
||||
None => archive.by_index(i),
|
||||
Some(pwd) => archive.by_index_decrypt(i, pwd.as_bytes())
|
||||
}.map_err(|e| e)?;
|
||||
Some(pwd) => archive.by_index_decrypt(i, pwd.as_bytes()),
|
||||
}
|
||||
.map_err(|e| e)?;
|
||||
|
||||
// create all pending dirs
|
||||
while let Some(pending_dir) = pending_directory_creates.pop_front() {
|
||||
|
|
@ -322,7 +323,9 @@ async fn copy_or_move(
|
|||
});
|
||||
}
|
||||
|
||||
context.recursive_copy_or_move(from_to_pairs, moving).map_err(OperationError::from_str)?;
|
||||
context
|
||||
.recursive_copy_or_move(from_to_pairs, moving)
|
||||
.map_err(OperationError::from_str)?;
|
||||
|
||||
Ok(context.op_sel)
|
||||
})
|
||||
|
|
@ -446,7 +449,7 @@ pub enum Operation {
|
|||
paths: Vec<PathBuf>,
|
||||
to: PathBuf,
|
||||
archive_type: ArchiveType,
|
||||
password: Option<String>
|
||||
password: Option<String>,
|
||||
},
|
||||
/// Copy items
|
||||
Copy {
|
||||
|
|
@ -463,7 +466,7 @@ pub enum Operation {
|
|||
Extract {
|
||||
paths: Vec<PathBuf>,
|
||||
to: PathBuf,
|
||||
password: Option<String>
|
||||
password: Option<String>,
|
||||
},
|
||||
/// Move items
|
||||
Move {
|
||||
|
|
@ -493,7 +496,7 @@ pub enum Operation {
|
|||
#[derive(Clone, Debug)]
|
||||
pub enum OperationErrorType {
|
||||
Generic(String),
|
||||
PasswordRequired
|
||||
PasswordRequired,
|
||||
}
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OperationError {
|
||||
|
|
@ -512,7 +515,7 @@ impl std::fmt::Display for OperationError {
|
|||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.kind {
|
||||
OperationErrorType::Generic(s) => s.fmt(f),
|
||||
OperationErrorType::PasswordRequired => f.write_str("Password required")
|
||||
OperationErrorType::PasswordRequired => f.write_str("Password required"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -548,7 +551,11 @@ impl Operation {
|
|||
progress = progress()
|
||||
),
|
||||
Self::EmptyTrash => fl!("emptying-trash", progress = progress()),
|
||||
Self::Extract { paths, to, password: _ } => fl!(
|
||||
Self::Extract {
|
||||
paths,
|
||||
to,
|
||||
password: _,
|
||||
} => fl!(
|
||||
"extracting",
|
||||
items = paths.len(),
|
||||
from = paths_parent_name(paths),
|
||||
|
|
@ -603,7 +610,11 @@ impl Operation {
|
|||
to = fl!("trash")
|
||||
),
|
||||
Self::EmptyTrash => fl!("emptied-trash"),
|
||||
Self::Extract { paths, to, password: _ } => fl!(
|
||||
Self::Extract {
|
||||
paths,
|
||||
to,
|
||||
password: _,
|
||||
} => fl!(
|
||||
"extracted",
|
||||
items = paths.len(),
|
||||
from = paths_parent_name(paths),
|
||||
|
|
@ -674,123 +685,147 @@ impl Operation {
|
|||
paths,
|
||||
to,
|
||||
archive_type,
|
||||
password
|
||||
password,
|
||||
} => {
|
||||
tokio::task::spawn_blocking(move || -> Result<OperationSelection, OperationError> {
|
||||
let Some(relative_root) = to.parent() else {
|
||||
return Err(OperationError::from_str(format!("path {:?} has no parent directory", to)));
|
||||
};
|
||||
tokio::task::spawn_blocking(
|
||||
move || -> Result<OperationSelection, OperationError> {
|
||||
let Some(relative_root) = to.parent() else {
|
||||
return Err(OperationError::from_str(format!(
|
||||
"path {:?} has no parent directory",
|
||||
to
|
||||
)));
|
||||
};
|
||||
|
||||
let op_sel = OperationSelection {
|
||||
ignored: paths.clone(),
|
||||
selected: vec![to.clone()],
|
||||
};
|
||||
let op_sel = OperationSelection {
|
||||
ignored: paths.clone(),
|
||||
selected: vec![to.clone()],
|
||||
};
|
||||
|
||||
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(OperationError::from_str)?;
|
||||
paths.push(entry.into_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match archive_type {
|
||||
ArchiveType::Tgz => {
|
||||
let mut archive = fs::File::create(&to)
|
||||
.map(io::BufWriter::new)
|
||||
.map(|w| {
|
||||
flate2::write::GzEncoder::new(w, flate2::Compression::default())
|
||||
})
|
||||
.map(tar::Builder::new)
|
||||
.map_err(OperationError::from_str)?;
|
||||
|
||||
let total_paths = paths.len();
|
||||
for (i, path) in paths.iter().enumerate() {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
|
||||
controller.set_progress((i as f32) / total_paths as f32);
|
||||
|
||||
if let Some(relative_path) =
|
||||
path.strip_prefix(relative_root).map_err(OperationError::from_str)?.to_str()
|
||||
{
|
||||
archive
|
||||
.append_path_with_name(path, relative_path)
|
||||
.map_err(OperationError::from_str)?;
|
||||
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(OperationError::from_str)?;
|
||||
paths.push(entry.into_path());
|
||||
}
|
||||
}
|
||||
|
||||
archive.finish().map_err(OperationError::from_str)?;
|
||||
}
|
||||
ArchiveType::Zip => {
|
||||
let mut archive = fs::File::create(&to)
|
||||
.map(io::BufWriter::new)
|
||||
.map(zip::ZipWriter::new)
|
||||
.map_err(OperationError::from_str)?;
|
||||
|
||||
let total_paths = paths.len();
|
||||
let mut buffer = vec![0; 4 * 1024 * 1024];
|
||||
for (i, path) in paths.iter().enumerate() {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
match archive_type {
|
||||
ArchiveType::Tgz => {
|
||||
let mut archive = fs::File::create(&to)
|
||||
.map(io::BufWriter::new)
|
||||
.map(|w| {
|
||||
flate2::write::GzEncoder::new(
|
||||
w,
|
||||
flate2::Compression::default(),
|
||||
)
|
||||
})
|
||||
.map(tar::Builder::new)
|
||||
.map_err(OperationError::from_str)?;
|
||||
|
||||
controller.set_progress((i as f32) / total_paths as f32);
|
||||
let total_paths = paths.len();
|
||||
for (i, path) in paths.iter().enumerate() {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
|
||||
let mut zip_options = zip::write::SimpleFileOptions::default();
|
||||
if password.is_some() {
|
||||
zip_options = zip_options.with_aes_encryption(Aes256, password.as_deref().unwrap());
|
||||
}
|
||||
if let Some(relative_path) =
|
||||
path.strip_prefix(relative_root).map_err(OperationError::from_str)?.to_str()
|
||||
{
|
||||
if path.is_file() {
|
||||
let mut file = fs::File::open(path).map_err(OperationError::from_str)?;
|
||||
let metadata = file.metadata().map_err(OperationError::from_str)?;
|
||||
let total = metadata.len();
|
||||
if total >= 4 * 1024 * 1024 * 1024 {
|
||||
// The large file option must be enabled for files above 4 GiB
|
||||
zip_options = zip_options.large_file(true);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
let mode = metadata.mode();
|
||||
zip_options = zip_options.unix_permissions(mode);
|
||||
}
|
||||
controller.set_progress((i as f32) / total_paths as f32);
|
||||
|
||||
if let Some(relative_path) = path
|
||||
.strip_prefix(relative_root)
|
||||
.map_err(OperationError::from_str)?
|
||||
.to_str()
|
||||
{
|
||||
archive
|
||||
.start_file(relative_path, zip_options)
|
||||
.map_err(OperationError::from_str)?;
|
||||
let mut current = 0;
|
||||
loop {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
|
||||
let count = file.read(&mut buffer).map_err(OperationError::from_str)?;
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
archive.write_all(&buffer[..count]).map_err(OperationError::from_str)?;
|
||||
current += count;
|
||||
|
||||
let file_progress = current as f32 / total as f32;
|
||||
let total_progress =
|
||||
(i as f32 + file_progress) / total_paths as f32;
|
||||
controller.set_progress(total_progress);
|
||||
}
|
||||
} else {
|
||||
archive
|
||||
.add_directory(relative_path, zip_options)
|
||||
.append_path_with_name(path, relative_path)
|
||||
.map_err(OperationError::from_str)?;
|
||||
}
|
||||
}
|
||||
|
||||
archive.finish().map_err(OperationError::from_str)?;
|
||||
}
|
||||
ArchiveType::Zip => {
|
||||
let mut archive = fs::File::create(&to)
|
||||
.map(io::BufWriter::new)
|
||||
.map(zip::ZipWriter::new)
|
||||
.map_err(OperationError::from_str)?;
|
||||
|
||||
archive.finish().map_err(OperationError::from_str)?;
|
||||
let total_paths = paths.len();
|
||||
let mut buffer = vec![0; 4 * 1024 * 1024];
|
||||
for (i, path) in paths.iter().enumerate() {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
|
||||
controller.set_progress((i as f32) / total_paths as f32);
|
||||
|
||||
let mut zip_options = zip::write::SimpleFileOptions::default();
|
||||
if password.is_some() {
|
||||
zip_options = zip_options.with_aes_encryption(
|
||||
Aes256,
|
||||
password.as_deref().unwrap(),
|
||||
);
|
||||
}
|
||||
if let Some(relative_path) = path
|
||||
.strip_prefix(relative_root)
|
||||
.map_err(OperationError::from_str)?
|
||||
.to_str()
|
||||
{
|
||||
if path.is_file() {
|
||||
let mut file = fs::File::open(path)
|
||||
.map_err(OperationError::from_str)?;
|
||||
let metadata = file
|
||||
.metadata()
|
||||
.map_err(OperationError::from_str)?;
|
||||
let total = metadata.len();
|
||||
if total >= 4 * 1024 * 1024 * 1024 {
|
||||
// The large file option must be enabled for files above 4 GiB
|
||||
zip_options = zip_options.large_file(true);
|
||||
}
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
let mode = metadata.mode();
|
||||
zip_options = zip_options.unix_permissions(mode);
|
||||
}
|
||||
archive
|
||||
.start_file(relative_path, zip_options)
|
||||
.map_err(OperationError::from_str)?;
|
||||
let mut current = 0;
|
||||
loop {
|
||||
controller
|
||||
.check()
|
||||
.map_err(OperationError::from_str)?;
|
||||
|
||||
let count = file
|
||||
.read(&mut buffer)
|
||||
.map_err(OperationError::from_str)?;
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
archive
|
||||
.write_all(&buffer[..count])
|
||||
.map_err(OperationError::from_str)?;
|
||||
current += count;
|
||||
|
||||
let file_progress = current as f32 / total as f32;
|
||||
let total_progress =
|
||||
(i as f32 + file_progress) / total_paths as f32;
|
||||
controller.set_progress(total_progress);
|
||||
}
|
||||
} else {
|
||||
archive
|
||||
.add_directory(relative_path, zip_options)
|
||||
.map_err(OperationError::from_str)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
archive.finish().map_err(OperationError::from_str)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(op_sel)
|
||||
})
|
||||
Ok(op_sel)
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(OperationError::from_str)?
|
||||
//.map_err(|e| e)?
|
||||
|
|
@ -830,7 +865,8 @@ impl Operation {
|
|||
|
||||
controller.set_progress(i as f32 / count as f32);
|
||||
|
||||
trash::os_limited::purge_all([item]).map_err(OperationError::from_str)?;
|
||||
trash::os_limited::purge_all([item])
|
||||
.map_err(OperationError::from_str)?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
|
@ -839,127 +875,141 @@ impl Operation {
|
|||
}
|
||||
Ok(OperationSelection::default())
|
||||
}
|
||||
Self::Extract { paths, to, password } => {
|
||||
tokio::task::spawn_blocking(move || -> Result<OperationSelection, OperationError> {
|
||||
let total_paths = paths.len();
|
||||
let mut op_sel = OperationSelection::default();
|
||||
for (i, path) in paths.iter().enumerate() {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
Self::Extract {
|
||||
paths,
|
||||
to,
|
||||
password,
|
||||
} => {
|
||||
tokio::task::spawn_blocking(
|
||||
move || -> Result<OperationSelection, OperationError> {
|
||||
let total_paths = paths.len();
|
||||
let mut op_sel = OperationSelection::default();
|
||||
for (i, path) in paths.iter().enumerate() {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
|
||||
controller.set_progress((i as f32) / total_paths as f32);
|
||||
controller.set_progress((i as f32) / total_paths as f32);
|
||||
|
||||
if let Some(file_name) = path.file_name().and_then(|f| f.to_str()) {
|
||||
let dir_name = get_directory_name(file_name);
|
||||
let mut new_dir = to.join(dir_name);
|
||||
if let Some(file_name) = path.file_name().and_then(|f| f.to_str()) {
|
||||
let dir_name = get_directory_name(file_name);
|
||||
let mut new_dir = to.join(dir_name);
|
||||
|
||||
if new_dir.exists() {
|
||||
if let Some(new_dir_parent) = new_dir.parent() {
|
||||
new_dir = copy_unique_path(&new_dir, new_dir_parent);
|
||||
if new_dir.exists() {
|
||||
if let Some(new_dir_parent) = new_dir.parent() {
|
||||
new_dir = copy_unique_path(&new_dir, new_dir_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
op_sel.ignored.push(path.clone());
|
||||
op_sel.selected.push(new_dir.clone());
|
||||
op_sel.ignored.push(path.clone());
|
||||
op_sel.selected.push(new_dir.clone());
|
||||
|
||||
let controller = controller.clone();
|
||||
let mime = mime_for_path(path);
|
||||
let password = password.clone();
|
||||
match mime.essence_str() {
|
||||
"application/gzip" | "application/x-compressed-tar" => {
|
||||
OpReader::new(path, controller)
|
||||
let controller = controller.clone();
|
||||
let mime = mime_for_path(path);
|
||||
let password = password.clone();
|
||||
match mime.essence_str() {
|
||||
"application/gzip" | "application/x-compressed-tar" => {
|
||||
OpReader::new(path, controller)
|
||||
.map(io::BufReader::new)
|
||||
.map(flate2::read::GzDecoder::new)
|
||||
.map(tar::Archive::new)
|
||||
.and_then(|mut archive| archive.unpack(&new_dir))
|
||||
.map_err(OperationError::from_str)?
|
||||
}
|
||||
"application/x-tar" => OpReader::new(path, controller)
|
||||
.map(io::BufReader::new)
|
||||
.map(flate2::read::GzDecoder::new)
|
||||
.map(tar::Archive::new)
|
||||
.and_then(|mut archive| archive.unpack(&new_dir))
|
||||
.map_err(OperationError::from_str)?,
|
||||
"application/zip" => fs::File::open(path)
|
||||
.map(io::BufReader::new)
|
||||
.map(zip::ZipArchive::new)
|
||||
.map_err(OperationError::from_str)?
|
||||
}
|
||||
"application/x-tar" => OpReader::new(path, controller)
|
||||
.map(io::BufReader::new)
|
||||
.map(tar::Archive::new)
|
||||
.and_then(|mut archive| archive.unpack(&new_dir))
|
||||
.map_err(OperationError::from_str)?,
|
||||
"application/zip" => fs::File::open(path)
|
||||
.map(io::BufReader::new)
|
||||
.map(zip::ZipArchive::new)
|
||||
.map_err(OperationError::from_str)?
|
||||
.and_then(move |mut archive| {
|
||||
zip_extract(&mut archive, &new_dir, controller, password)
|
||||
})
|
||||
.map_err(|e| match e {
|
||||
ZipError::UnsupportedArchive(ZipError::PASSWORD_REQUIRED) |
|
||||
ZipError::InvalidPassword => {
|
||||
OperationError {
|
||||
.and_then(move |mut archive| {
|
||||
zip_extract(
|
||||
&mut archive,
|
||||
&new_dir,
|
||||
controller,
|
||||
password,
|
||||
)
|
||||
})
|
||||
.map_err(|e| match e {
|
||||
ZipError::UnsupportedArchive(
|
||||
ZipError::PASSWORD_REQUIRED,
|
||||
)
|
||||
| ZipError::InvalidPassword => OperationError {
|
||||
kind: OperationErrorType::PasswordRequired,
|
||||
}
|
||||
},
|
||||
_ => OperationError::from_str(e)
|
||||
})?,
|
||||
#[cfg(feature = "bzip2")]
|
||||
"application/x-bzip" | "application/x-bzip-compressed-tar" => {
|
||||
OpReader::new(path, controller)
|
||||
.map(io::BufReader::new)
|
||||
.map(bzip2::read::BzDecoder::new)
|
||||
.map(tar::Archive::new)
|
||||
.and_then(|mut archive| archive.unpack(&new_dir))
|
||||
.map_err(OperationError::from_str)?
|
||||
},
|
||||
_ => OperationError::from_str(e),
|
||||
})?,
|
||||
#[cfg(feature = "bzip2")]
|
||||
"application/x-bzip" | "application/x-bzip-compressed-tar" => {
|
||||
OpReader::new(path, controller)
|
||||
.map(io::BufReader::new)
|
||||
.map(bzip2::read::BzDecoder::new)
|
||||
.map(tar::Archive::new)
|
||||
.and_then(|mut archive| archive.unpack(&new_dir))
|
||||
.map_err(OperationError::from_str)?
|
||||
}
|
||||
#[cfg(feature = "liblzma")]
|
||||
"application/x-xz" | "application/x-xz-compressed-tar" => {
|
||||
OpReader::new(path, controller)
|
||||
.map(io::BufReader::new)
|
||||
.map(liblzma::read::XzDecoder::new)
|
||||
.map(tar::Archive::new)
|
||||
.and_then(|mut archive| archive.unpack(&new_dir))
|
||||
.map_err(OperationError::from_str)?
|
||||
}
|
||||
_ => Err(OperationError::from_str(format!(
|
||||
"unsupported mime type {:?}",
|
||||
mime
|
||||
)))?,
|
||||
}
|
||||
#[cfg(feature = "liblzma")]
|
||||
"application/x-xz" | "application/x-xz-compressed-tar" => {
|
||||
OpReader::new(path, controller)
|
||||
.map(io::BufReader::new)
|
||||
.map(liblzma::read::XzDecoder::new)
|
||||
.map(tar::Archive::new)
|
||||
.and_then(|mut archive| archive.unpack(&new_dir))
|
||||
.map_err(OperationError::from_str)?
|
||||
}
|
||||
_ => Err(OperationError::from_str(format!("unsupported mime type {:?}", mime)))?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(op_sel)
|
||||
})
|
||||
Ok(op_sel)
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(OperationError::from_str)?
|
||||
//.map_err(OperationError::from_str)?
|
||||
}
|
||||
Self::Move { paths, to } => copy_or_move(paths, to, true, msg_tx, controller).await,
|
||||
Self::NewFolder { path } => {
|
||||
tokio::task::spawn_blocking(move || -> Result<OperationSelection, OperationError> {
|
||||
Self::NewFolder { path } => tokio::task::spawn_blocking(
|
||||
move || -> Result<OperationSelection, OperationError> {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
fs::create_dir(&path).map_err(OperationError::from_str)?;
|
||||
Ok(OperationSelection {
|
||||
ignored: Vec::new(),
|
||||
selected: vec![path],
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(OperationError::from_str)?
|
||||
}
|
||||
Self::NewFile { path } => {
|
||||
tokio::task::spawn_blocking(move || -> Result<OperationSelection, OperationError> {
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(OperationError::from_str)?,
|
||||
Self::NewFile { path } => tokio::task::spawn_blocking(
|
||||
move || -> Result<OperationSelection, OperationError> {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
fs::File::create(&path).map_err(OperationError::from_str)?;
|
||||
Ok(OperationSelection {
|
||||
ignored: Vec::new(),
|
||||
selected: vec![path],
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(OperationError::from_str)?
|
||||
}
|
||||
Self::Rename { from, to } => {
|
||||
tokio::task::spawn_blocking(move || -> Result<OperationSelection, OperationError> {
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(OperationError::from_str)?,
|
||||
Self::Rename { from, to } => tokio::task::spawn_blocking(
|
||||
move || -> Result<OperationSelection, OperationError> {
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
fs::rename(&from, &to).map_err(OperationError::from_str)?;
|
||||
Ok(OperationSelection {
|
||||
ignored: vec![from],
|
||||
selected: vec![to],
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(OperationError::from_str)?
|
||||
}
|
||||
},
|
||||
)
|
||||
.await
|
||||
.map_err(OperationError::from_str)?,
|
||||
#[cfg(target_os = "macos")]
|
||||
Self::Restore { .. } => {
|
||||
// TODO: add support for macos
|
||||
|
|
@ -995,7 +1045,9 @@ impl Operation {
|
|||
|
||||
controller.check().map_err(OperationError::from_str)?;
|
||||
|
||||
let mut perms = fs::metadata(&path).map_err(OperationError::from_str)?.permissions();
|
||||
let mut perms = fs::metadata(&path)
|
||||
.map_err(OperationError::from_str)?
|
||||
.permissions();
|
||||
let current_mode = perms.mode();
|
||||
let new_mode = current_mode | 0o111;
|
||||
perms.set_mode(new_mode);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue