Add support for compressing to .tgz, add gio network mount example

This commit is contained in:
Jeremy Soller 2024-09-10 11:50:14 -06:00
parent c4c92be708
commit 4374132e2f
No known key found for this signature in database
GPG key ID: D02FD439211AF56F
6 changed files with 219 additions and 135 deletions

View file

@ -303,19 +303,32 @@ impl ContextPage {
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub enum ArchiveType {
Tgz,
#[default]
Zip,
}
impl ArchiveType {
pub fn extension(&self) -> String {
pub fn all() -> &'static [Self] {
&[Self::Tgz, Self::Zip]
}
pub fn extension(&self) -> &str {
match self {
ArchiveType::Zip => ".zip".to_string(),
ArchiveType::Tgz => ".tgz",
ArchiveType::Zip => ".zip",
}
}
}
impl AsRef<str> for ArchiveType {
fn as_ref(&self) -> &str {
self.extension()
}
}
#[derive(Clone, Debug)]
pub enum DialogPage {
Compress {
@ -1299,7 +1312,7 @@ impl Application for App {
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;
let archive_type = ArchiveType::default();
self.dialog_pages.push_back(DialogPage::Compress {
paths,
to,
@ -2436,6 +2449,8 @@ impl Application for App {
}
};
let archive_types = ArchiveType::all();
let selected = archive_types.iter().position(|&x| x == *archive_type);
dialog
.primary_action(
widget::button::suggested(fl!("create"))
@ -2455,12 +2470,20 @@ impl Application for App {
paths: paths.clone(),
to: to.clone(),
name: name.clone(),
archive_type: archive_type.clone(),
archive_type: *archive_type,
})
})
.on_submit_maybe(complete_maybe)
.into(),
widget::text::body(".zip").into(),
widget::dropdown(archive_types, selected, move |index| {
Message::DialogUpdate(DialogPage::Compress {
paths: paths.clone(),
to: to.clone(),
name: name.clone(),
archive_type: archive_types[index],
})
})
.into(),
])
.align_items(Alignment::Center)
.spacing(space_xxs)

View file

@ -126,10 +126,14 @@ pub fn context_menu<'a>(
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"]
.iter()
.filter_map(|mime_type| mime_type.parse::<Mime>().ok())
.collect::<Vec<_>>();
let supported_archive_types = [
"application/x-compressed-tar",
"application/x-tar",
"application/zip",
]
.iter()
.filter_map(|mime_type| mime_type.parse::<Mime>().ok())
.collect::<Vec<_>>();
selected_types.retain(|t| !supported_archive_types.contains(t));
if selected_types.is_empty() {
children.push(menu_item(fl!("extract-here"), Action::ExtractHere).into());

View file

@ -15,7 +15,9 @@ use walkdir::WalkDir;
use crate::{
app::{ArchiveType, DialogPage, Message},
config::IconSizes,
fl, tab,
fl,
mime_icon::mime_for_path,
tab,
};
fn err_str<T: ToString>(err: T) -> String {
@ -374,26 +376,31 @@ impl Operation {
} => {
let msg_tx = msg_tx.clone();
tokio::task::spawn_blocking(move || -> Result<(), String> {
let Some(relative_root) = to.parent() else {
return Err(format!("path {:?} has no parent directory", to));
};
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());
}
}
}
match archive_type {
ArchiveType::Zip => {
ArchiveType::Tgz => {
let mut archive = fs::File::create(&to)
.map(io::BufWriter::new)
.map(zip::ZipWriter::new)
.map(|w| {
flate2::write::GzEncoder::new(w, flate2::Compression::default())
})
.map(tar::Builder::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 {
@ -405,27 +412,56 @@ impl Operation {
.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)?;
if let Some(relative_path) =
path.strip_prefix(relative_root).map_err(err_str)?.to_str()
{
archive
.append_path_with_name(path, relative_path)
.map_err(err_str)?;
}
}
let mut buffer = Vec::new();
let mut file = fs::File::open(&path)
.map(io::BufReader::new)
.map_err(err_str)?;
archive.finish().map_err(err_str)?;
}
ArchiveType::Zip => {
let mut archive = fs::File::create(&to)
.map(io::BufWriter::new)
.map(zip::ZipWriter::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)?;
}
//TODO: set unix_permissions per file?
let zip_options = zip::write::SimpleFileOptions::default();
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_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)?;
}
}
}
@ -611,21 +647,26 @@ impl Operation {
}
}
if let Some(mime) = mime_guess::from_path(&path).first() {
match mime.essence_str() {
"application/x-tar" => fs::File::open(path)
.map(io::BufReader::new)
.map(tar::Archive::new)
.and_then(|mut archive| archive.unpack(new_dir))
.map_err(err_str)?,
"application/zip" => fs::File::open(path)
.map(io::BufReader::new)
.map(zip::ZipArchive::new)
.map_err(err_str)?
.and_then(|mut archive| archive.extract(new_dir))
.map_err(err_str)?,
_ => Err(format!("unsupported mime type {:?}", mime))?,
}
let mime = mime_for_path(&path);
match mime.essence_str() {
"application/x-compressed-tar" => fs::File::open(path)
.map(io::BufReader::new)
.map(flate2::read::GzDecoder::new)
.map(tar::Archive::new)
.and_then(|mut archive| archive.unpack(new_dir))
.map_err(err_str)?,
"application/x-tar" => fs::File::open(path)
.map(io::BufReader::new)
.map(tar::Archive::new)
.and_then(|mut archive| archive.unpack(new_dir))
.map_err(err_str)?,
"application/zip" => fs::File::open(path)
.map(io::BufReader::new)
.map(zip::ZipArchive::new)
.map_err(err_str)?
.and_then(|mut archive| archive.extract(new_dir))
.map_err(err_str)?,
_ => Err(format!("unsupported mime type {:?}", mime))?,
}
}
}