diff options
-rw-r--r-- | src/action_map.rs | 4 | ||||
-rw-r--r-- | src/compress.rs | 52 | ||||
-rw-r--r-- | src/event_exec.rs | 52 | ||||
-rw-r--r-- | src/fm_error.rs | 7 | ||||
-rw-r--r-- | src/keybindings.rs | 1 | ||||
-rw-r--r-- | src/main.rs | 15 | ||||
-rw-r--r-- | src/mode.rs | 3 | ||||
-rw-r--r-- | src/status.rs | 6 | ||||
-rw-r--r-- | src/term_manager.rs | 24 |
9 files changed, 147 insertions, 17 deletions
diff --git a/src/action_map.rs b/src/action_map.rs index c3a85c9..5800f3a 100644 --- a/src/action_map.rs +++ b/src/action_map.rs @@ -17,6 +17,7 @@ pub enum ActionMap { Bulkrename, Chmod, ClearFlags, + Compress, CopyFilename, CopyFilepath, CopyPaste, @@ -50,6 +51,7 @@ pub enum ActionMap { NewFile, Nothing, NvimFilepicker, + OpenConfig, OpenFile, PageDown, PageUp, @@ -78,7 +80,6 @@ pub enum ActionMap { TreeFold, TreeUnFoldAll, TreeFoldAll, - OpenConfig, } impl ActionMap { @@ -93,6 +94,7 @@ impl ActionMap { ActionMap::Bulkrename => EventExec::event_bulkrename(status), ActionMap::Chmod => EventExec::event_chmod(status), ActionMap::ClearFlags => EventExec::event_clear_flags(status), + ActionMap::Compress => EventExec::event_compress(status), ActionMap::CopyFilename => EventExec::event_copy_filename(status), ActionMap::CopyFilepath => EventExec::event_copy_filepath(status), ActionMap::CopyPaste => EventExec::event_copy_paste(status), diff --git a/src/compress.rs b/src/compress.rs index aa8ab13..af7792f 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -1,10 +1,44 @@ +use std::fs::File; +use std::io::prelude::*; +use std::io::Write; + use flate2::write::{DeflateEncoder, GzEncoder, ZlibEncoder}; use flate2::Compression; -use std::fs::File; use tar::Builder; +use zip::write::FileOptions; -// use crate::fileinfo::FileInfo; use crate::fm_error::FmResult; +use crate::impl_selectable_content; + +#[derive(Debug)] +pub enum CompressionMethod { + DEFLATE, + GZ, + ZLIB, + ZIP, +} + +#[derive(Debug)] +pub struct CompressionPicker { + content: Vec<CompressionMethod>, + pub index: usize, +} + +impl CompressionPicker { + pub fn new() -> Self { + Self { + content: vec![ + CompressionMethod::DEFLATE, + CompressionMethod::GZ, + CompressionMethod::ZLIB, + CompressionMethod::ZIP, + ], + index: 0, + } + } +} + +impl_selectable_content!(CompressionMethod, CompressionPicker); pub fn compressed_gzip(archive_name: String, files: Vec<std::path::PathBuf>) -> FmResult<()> { let compressed_file = File::create(archive_name)?; @@ -71,3 +105,17 @@ pub fn compressed_zlib(archive_name: String, files: Vec<std::path::PathBuf>) -> Ok(()) } + +pub fn compressed_zip(archive_name: String, files: Vec<std::path::PathBuf>) -> FmResult<()> { + let archive = std::fs::File::create(&archive_name).unwrap(); + let mut zip = zip::ZipWriter::new(archive); + for file in files.iter() { + let mut buffer = Vec::new(); + zip.start_file(file.to_str().unwrap(), FileOptions::default())?; + let mut content = File::open(file)?; + content.read_to_end(&mut buffer)?; + zip.write_all(&buffer)?; + } + zip.finish()?; + Ok(()) +} diff --git a/src/event_exec.rs b/src/event_exec.rs index 061291a..428b973 100644 --- a/src/event_exec.rs +++ b/src/event_exec.rs @@ -9,6 +9,9 @@ use sysinfo::SystemExt; use crate::bulkrename::Bulkrename; use crate::completion::InputCompleted; +use crate::compress::{ + compressed_deflate, compressed_gzip, compressed_zip, compressed_zlib, CompressionMethod, +}; use crate::config::Colors; use crate::constant_strings_paths::CONFIG_PATH; use crate::constant_strings_paths::DEFAULT_DRAGNDROP; @@ -1170,6 +1173,7 @@ impl EventExec { Mode::Navigate(Navigate::Trash) => EventExec::event_trash_prev(status), Mode::Navigate(Navigate::Shortcut) => EventExec::event_shortcut_prev(status.selected()), Mode::Navigate(Navigate::Marks(_)) => EventExec::event_marks_prev(status), + Mode::Navigate(Navigate::Compress) => EventExec::event_compression_prev(status), Mode::Navigate(Navigate::EncryptedDrive) => { EventExec::event_encrypted_drive_prev(status) } @@ -1192,6 +1196,7 @@ impl EventExec { Mode::Navigate(Navigate::Trash) => EventExec::event_trash_next(status), Mode::Navigate(Navigate::Shortcut) => EventExec::event_shortcut_next(status.selected()), Mode::Navigate(Navigate::Marks(_)) => EventExec::event_marks_next(status), + Mode::Navigate(Navigate::Compress) => EventExec::event_compression_next(status), Mode::Navigate(Navigate::EncryptedDrive) => { EventExec::event_encrypted_drive_next(status) } @@ -1347,6 +1352,7 @@ impl EventExec { Mode::Navigate(Navigate::Marks(MarkAction::Jump)) => { EventExec::exec_marks_jump_selected(status)? } + Mode::Navigate(Navigate::Compress) => EventExec::exec_compress(status)?, }; status.selected().input.reset(); @@ -1724,6 +1730,52 @@ impl EventExec { } Ok(()) } + + pub fn event_compress(status: &mut Status) -> FmResult<()> { + status + .selected() + .set_mode(Mode::Navigate(Navigate::Compress)); + Ok(()) + } + + pub fn exec_compress(status: &mut Status) -> FmResult<()> { + let cwd = std::env::current_dir()?; + let files = status + .flagged + .content + .iter() + .map(|abs_path| pathdiff::diff_paths(abs_path, &cwd)) + .filter(|p| p.is_some()) + .map(|p| p.unwrap()) + .collect(); + match status.compression.selected() { + Some(CompressionMethod::DEFLATE) => { + let archive_name = "archive.tar.gz".to_owned(); + compressed_gzip(archive_name, files) + } + Some(CompressionMethod::GZ) => { + let archive_name = "archive.tar.gz".to_owned(); + compressed_deflate(archive_name, files) + } + Some(CompressionMethod::ZLIB) => { + let archive_name = "archive.tar.xz".to_owned(); + compressed_zlib(archive_name, files) + } + Some(CompressionMethod::ZIP) => { + let archive_name = "archive.zip".to_owned(); + compressed_zip(archive_name, files) + } + None => Ok(()), + } + } + + pub fn event_compression_prev(status: &mut Status) { + status.compression.prev() + } + + pub fn event_compression_next(status: &mut Status) { + status.compression.next() + } } fn string_to_path(path_string: &str) -> FmResult<path::PathBuf> { diff --git a/src/fm_error.rs b/src/fm_error.rs index bc7352f..f811822 100644 --- a/src/fm_error.rs +++ b/src/fm_error.rs @@ -30,6 +30,7 @@ pub enum ErrorVariant { SERDEYAML, CHRONO, UTF8ERROR, + ZIPERROR, CUSTOM(String), } @@ -187,5 +188,11 @@ impl From<std::string::FromUtf8Error> for FmError { } } +impl From<zip::result::ZipError> for FmError { + fn from(error: zip::result::ZipError) -> Self { + Self::new(ErrorVariant::ZIPERROR, &error.to_string()) + } +} + /// A Result with type `T` and `FmError`. pub type FmResult<T> = Result<T, FmError>; diff --git a/src/keybindings.rs b/src/keybindings.rs index 1cac624..c2cbc4a 100644 --- a/src/keybindings.rs +++ b/src/keybindings.rs @@ -45,6 +45,7 @@ impl Bindings { (Key::Char('-'), ActionMap::Back), (Key::Char('~'), ActionMap::Home), (Key::Char('B'), ActionMap::Bulkrename), + (Key::Char('C'), ActionMap::Compress), (Key::Char('D'), ActionMap::ToggleDualPane), (Key::Char('F'), ActionMap::Filter), (Key::Char('G'), ActionMap::Shortcut), diff --git a/src/main.rs b/src/main.rs index a22b8ef..5807e28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use fm::utils::{drop_everything, init_term, print_on_quit}; /// Init the status and display and listen to events (keyboard, mouse, resize, custom...). /// The application is redrawn after every event. /// When the user issues a quit event, the main loop is broken and we reset the cursor. -fn main2() -> FmResult<()> { +fn main() -> FmResult<()> { set_logger()?; info!("fm is starting"); @@ -56,16 +56,3 @@ fn main2() -> FmResult<()> { info!("fm is shutting down"); Ok(()) } - -fn main() -> FmResult<()> { - use fm::compress::compressed_gzip; - use std::path::PathBuf; - - compressed_gzip( - "archive.tar.gz".to_owned(), - vec![ - PathBuf::from("compress_example/2022_nsi_metropole_j1_reseau_1.png"), - PathBuf::from("compress_example/2022_nsi_metropole_j1_correction.pdf"), - ], - ) -} diff --git a/src/mode.rs b/src/mode.rs index 26b2b38..10e2e84 100644 --- a/src/mode.rs +++ b/src/mode.rs @@ -94,6 +94,8 @@ pub enum Navigate { EncryptedDrive, /// Jump to a saved mark Marks(MarkAction), + /// Pick a compression method + Compress, } /// Different mode in which the application can be. @@ -144,6 +146,7 @@ impl fmt::Display for Mode { Mode::Navigate(Navigate::History) => write!(f, "History :"), Mode::Navigate(Navigate::Shortcut) => write!(f, "Shortcut :"), Mode::Navigate(Navigate::Trash) => write!(f, "Trash :"), + Mode::Navigate(Navigate::Compress) => write!(f, "Compress :"), Mode::Navigate(Navigate::EncryptedDrive) => { write!(f, "Encrypted devices :") } diff --git a/src/status.rs b/src/status.rs index 05ef219..3702635 100644 --- a/src/status.rs +++ b/src/status.rs @@ -10,6 +10,7 @@ use tuikit::term::Term; use users::UsersCache; use crate::args::Args; +use crate::compress::CompressionPicker; use crate::constant_strings_paths::OPENER_PATH; use crate::copy_move::{copy_move, CopyMove}; use crate::cryptsetup::DeviceOpener; @@ -57,7 +58,10 @@ pub struct Status { pub help: String, /// The trash pub trash: Trash, + /// Encrypted devices opener pub encrypted_devices: DeviceOpener, + /// Compression methods + pub compression: CompressionPicker, } impl Status { @@ -88,6 +92,7 @@ impl Status { tab2.shortcut .extend_with_mount_points(&Self::disks_mounts(sys.disks())); let trash = Trash::new()?; + let compression = CompressionPicker::new(); Ok(Self { tabs: [tab2, tab], @@ -103,6 +108,7 @@ impl Status { help, trash, encrypted_devices, + compression, }) } diff --git a/src/term_manager.rs b/src/term_manager.rs index 2238e8f..d870d66 100644 --- a/src/term_manager.rs +++ b/src/term_manager.rs @@ -9,6 +9,7 @@ use tuikit::event::Event; use tuikit::prelude::*; use tuikit::term::Term; +use crate::compress::CompressionMethod; use crate::config::Colors; use crate::constant_strings_paths::{ FILTER_PRESENTATION, HELP_FIRST_SENTENCE, HELP_SECOND_SENTENCE, @@ -376,6 +377,7 @@ impl<'a> Draw for WinSecondary<'a> { Mode::Navigate(Navigate::History) => self.destination(canvas, &self.tab.history), Mode::Navigate(Navigate::Shortcut) => self.destination(canvas, &self.tab.shortcut), Mode::Navigate(Navigate::Trash) => self.trash(canvas, &self.status.trash), + Mode::Navigate(Navigate::Compress) => self.compress(canvas, &self.status.compression), Mode::Navigate(Navigate::EncryptedDrive) => { self.encrypted_devices(self.status, self.tab, canvas) } @@ -521,6 +523,28 @@ impl<'a> WinSecondary<'a> { Ok(()) } + fn compress( + &self, + canvas: &mut dyn Canvas, + selectable: &impl SelectableContent<CompressionMethod>, + ) -> FmResult<()> { + canvas.print(1, 0, "Pick a compression method")?; + for (row, compression_method) in selectable.content().iter().enumerate() { + let mut attr = Attr::default(); + if row == selectable.index() { + attr.effect |= Effect::REVERSE; + } + + let _ = canvas.print_with_attr( + row + ContentWindow::WINDOW_MARGIN_TOP, + 4, + &format!("{compression_method:?}"), + attr, + ); + } + Ok(()) + } + fn marks(&self, status: &Status, tab: &Tab, canvas: &mut dyn Canvas) -> FmResult<()> { canvas.print_with_attr(2, 1, "mark path", Self::ATTR_YELLOW)?; |