diff options
author | qkzk <qkzk@users.noreply.github.com> | 2023-02-17 20:41:52 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-17 20:41:52 +0100 |
commit | 4d3093c4c7c7751fc78ab78f00b6db138e2fe292 (patch) | |
tree | fc64b7c963f4f9238d7bb329b81da19f1594a214 | |
parent | 494cd8494fbdf6d89043933ec5cbfd78b5c848b4 (diff) | |
parent | ee10361122bb1c75ba978a04f7a2c401beae8eae (diff) |
Merge pull request #71 from qkzk/better-tree-explorationv0.1.17
Better tree exploration
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Cargo.lock | 256 | ||||
-rw-r--r-- | Cargo.toml | 8 | ||||
-rw-r--r-- | development.md | 40 | ||||
-rw-r--r-- | src/action_map.rs | 18 | ||||
-rw-r--r-- | src/completion.rs | 18 | ||||
-rw-r--r-- | src/compress.rs | 178 | ||||
-rw-r--r-- | src/decompress.rs | 65 | ||||
-rw-r--r-- | src/event_dispatch.rs | 12 | ||||
-rw-r--r-- | src/event_exec.rs | 205 | ||||
-rw-r--r-- | src/fm_error.rs | 21 | ||||
-rw-r--r-- | src/git.rs | 18 | ||||
-rw-r--r-- | src/help.rs | 4 | ||||
-rw-r--r-- | src/keybindings.rs | 4 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/marks.rs | 61 | ||||
-rw-r--r-- | src/mode.rs | 16 | ||||
-rw-r--r-- | src/opener.rs | 33 | ||||
-rw-r--r-- | src/preview.rs | 4 | ||||
-rw-r--r-- | src/status.rs | 6 | ||||
-rw-r--r-- | src/tab.rs | 3 | ||||
-rw-r--r-- | src/term_manager.rs | 43 | ||||
-rw-r--r-- | src/tree.rs | 52 |
23 files changed, 908 insertions, 159 deletions
@@ -1,3 +1,4 @@ /target *.log *.vimspector* +*compress_example @@ -18,6 +18,18 @@ dependencies = [ ] [[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + +[[package]] name = "aho-corasick" version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -186,6 +198,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] +name = "base64ct" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" + +[[package]] name = "beef" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -246,10 +264,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] name = "cc" version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" +dependencies = [ + "jobserver", +] [[package]] name = "cfg-if" @@ -273,6 +315,15 @@ dependencies = [ ] [[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] name = "clap" version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -351,18 +402,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] -name = "compress-tools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3b1511783270e75d95749c851008a01fba662a8136e488d29d2b3a51a56e08" -dependencies = [ - "derive_more", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] name = "concurrent-queue" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -391,6 +430,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" [[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] name = "content_inspector" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -646,17 +691,6 @@ dependencies = [ ] [[package]] -name = "derive_more" -version = "0.99.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] name = "destructure_traitobject" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -670,6 +704,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -911,6 +946,18 @@ dependencies = [ ] [[package]] +name = "filetime" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + +[[package]] name = "flate2" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -935,13 +982,13 @@ dependencies = [ [[package]] name = "fm-tui" -version = "0.1.16" +version = "0.1.17" dependencies = [ "chrono", "clap 4.0.32", - "compress-tools", "content_inspector", "copypasta", + "flate2", "fs_extra", "image", "indicatif", @@ -949,9 +996,11 @@ dependencies = [ "log", "log4rs", "notify-rust", + "pathdiff", "pdf-extract", "rand", "regex", + "rust-lzma", "sanitize-filename", "serde_yaml 0.9.16", "shellexpand", @@ -961,9 +1010,11 @@ dependencies = [ "strum_macros 0.24.3", "syntect", "sysinfo", + "tar", "tuikit", "url-escape", "users", + "zip", ] [[package]] @@ -1128,6 +1179,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] name = "humantime" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1249,6 +1309,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] +name = "jobserver" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" +dependencies = [ + "libc", +] + +[[package]] name = "jpeg-decoder" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1675,6 +1744,12 @@ dependencies = [ ] [[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] name = "ordered-float" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1729,6 +1804,35 @@ dependencies = [ ] [[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core", + "subtle", +] + +[[package]] +name = "pathdiff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest", + "hmac", + "password-hash", + "sha2", +] + +[[package]] name = "pdf-extract" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2034,6 +2138,15 @@ dependencies = [ ] [[package]] +name = "rust-lzma" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "895dc04daeaeee338bb96e229797902ed3f0675bfc59d5b42e0f0b0c13ac54da" +dependencies = [ + "pkg-config", +] + +[[package]] name = "rustc_version" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2236,6 +2349,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" [[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] name = "shellexpand" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2473,6 +2597,12 @@ dependencies = [ ] [[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] name = "syn" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2522,6 +2652,17 @@ dependencies = [ ] [[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] name = "tauri-winrt-notification" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2892,12 +3033,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" [[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] name = "vec_map" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3265,6 +3400,15 @@ dependencies = [ ] [[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + +[[package]] name = "xcb" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3363,6 +3507,56 @@ dependencies = [ ] [[package]] +name = "zip" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0445d0fbc924bb93539b4316c11afb121ea39296f99a3c4c9edad09e3658cdef" +dependencies = [ + "aes", + "byteorder", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "flate2", + "hmac", + "pbkdf2", + "sha1 0.10.5", + "time 0.3.17", + "zstd", +] + +[[package]] +name = "zstd" +version = "0.11.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "5.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" +dependencies = [ + "libc", + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.7+zstd.1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94509c3ba2fe55294d752b79842c530ccfab760192521df74a081a78d2b3c7f5" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + +[[package]] name = "zvariant" version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1,6 +1,6 @@ [package] name = "fm-tui" -version = "0.1.16" +version = "0.1.17" authors = ["Quentin Konieczko <qu3nt1n@gmail.com>"] edition = "2021" license-file = "LICENSE.txt" @@ -31,9 +31,9 @@ fs_extra = "1.2.0" [dependencies] chrono = "0.4.22" clap = { version = "4.0.2", features = ["derive"] } -compress-tools = "0.14.0" content_inspector = "0.2.4" copypasta = "0.8.1" +flate2 = "1.0" fs_extra = "1.2.0" image = "0.24.5" indicatif = { version = "0.17.1", features= ["in_memory"] } @@ -41,9 +41,11 @@ kamadak-exif = "0.5.5" log = { version = "0.4.0", features = ["std"] } log4rs = {version = "1.2.0", features = ["rolling_file_appender", "compound_policy", "size_trigger", "fixed_window_roller"] } notify-rust = "4.5.10" +pathdiff = "0.2.1" pdf-extract = "0.6.4" rand = "0.8.5" regex = "1.6.0" +rust-lzma = "0.5.1" sanitize-filename = "0.4.0" serde_yaml = "0.9.13" shellexpand = "2.1.2" @@ -53,6 +55,8 @@ strum = {version = "0.24.1", features = ["derive"]} strum_macros = "0.24.3" syntect = "5.0.0" sysinfo = "0.26.7" +tar = "0.4.38" tuikit = "0.5.0" url-escape = "0.1.1" users = "0.11.0" +zip = "0.6.4" diff --git a/development.md b/development.md index f1ce896..bd6eb03 100644 --- a/development.md +++ b/development.md @@ -340,6 +340,29 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally. - [x] Add a shortcut to the config folder - [x] use g to go to the mounted encrypted drive +### Version 0.1.16 : fix completion & filter in tree + +- [x] FIX: can't parse uid gid if they only exists on a remote machine. See https://serverfault.com/questions/514118/mapping-uid-and-gid-of-local-user-to-the-mounted-nfs-share + for a fix. +- [x] FIX: truncate file size in preview mode. +- [x] FIX: in tree mode search is backward +- [x] FIX: when searching from tree mode, it only completes with level 1 elements, not nested ones. +- [x] FIX: when exiting search in tree mode, second line isn't updated +- [x] FIX: when filtering in tree mode, only the level 1 matching elements are displayed + Decided to keep directories when filtering in tree mode. Those are excluded when filtering in normal mode. +- [x] Tree: move 10 rows at a time + +### Version 0.1.17 : git root, navigable marks, compression/decompression, command mode, lazygit + +- [x] git root: cd to git root +- [x] tree: use the length of the screen to avoid parsing non displayed lines +- [x] navigate in marks: pick a mark and jump to it with enter +- [x] compress flagged files: pick a compression algorithm from a list. +- [x] decompress any archive we can create +- [x] command mode with ":" and a command. (ie `:ClearFlags`). Command are completed. + some commands does nothing :( +- [x] lazygit integration: open a terminal with lazygit in current path. Obviously (lazygit)[https://github.com/jesseduffield/lazygit] should be installed. + ## TODO - [ ] remote control @@ -361,20 +384,9 @@ New view: Tree ! Toggle with 't', fold with 'z'. Navigate normally. - [ ] vim keys, harmonize keybinds with ranger - [ ] zoxide support - -- [ ] scrollable shortcuts, marks & history - -- [ ] Version 0.1.16 : fix completion & filter in tree - - - [x] FIX: can't parse uid gid if they only exists on a remote machine. See https://serverfault.com/questions/514118/mapping-uid-and-gid-of-local-user-to-the-mounted-nfs-share - for a fix. - - [x] FIX: truncate file size in preview mode. - - [x] FIX: in tree mode search is backward - - [x] FIX: when searching from tree mode, it only completes with level 1 elements, not nested ones. - - [x] FIX: when exiting search in tree mode, second line isn't updated - - [x] FIX: when filtering in tree mode, only the level 1 matching elements are displayed - Decided to keep directories when filtering in tree mode. Those are excluded when filtering in normal mode. - - [x] Tree: move 10 rows at a time +- [ ] make navigable content scrollable +- [ ] temporary marks +- [ ] context switch - [ ] Version 0.1.50 : safety & memory usage diff --git a/src/action_map.rs b/src/action_map.rs index f84d60b..d3fef16 100644 --- a/src/action_map.rs +++ b/src/action_map.rs @@ -1,4 +1,4 @@ -use strum_macros::{Display, EnumString}; +use strum_macros::{Display, EnumIter, EnumString}; use crate::config::Colors; use crate::event_exec::EventExec; @@ -9,7 +9,7 @@ use crate::status::Status; /// All those actions are mapped to a key and this enum /// makes the junction between received Key events and /// actions in the application. -#[derive(Clone, Debug, Display, EnumString)] +#[derive(Clone, Debug, Display, EnumString, EnumIter)] pub enum ActionMap { Back, BackTab, @@ -17,6 +17,8 @@ pub enum ActionMap { Bulkrename, Chmod, ClearFlags, + Command, + Compress, CopyFilename, CopyFilepath, CopyPaste, @@ -32,12 +34,14 @@ pub enum ActionMap { Filter, FlagAll, FuzzyFind, + GitRoot, Goto, Help, History, Home, Jump, KeyHome, + Lazygit, MarksJump, MarksNew, ModeNormal, @@ -49,6 +53,7 @@ pub enum ActionMap { NewFile, Nothing, NvimFilepicker, + OpenConfig, OpenFile, PageDown, PageUp, @@ -77,7 +82,6 @@ pub enum ActionMap { TreeFold, TreeUnFoldAll, TreeFoldAll, - OpenConfig, } impl ActionMap { @@ -92,6 +96,8 @@ impl ActionMap { ActionMap::Bulkrename => EventExec::event_bulkrename(status), ActionMap::Chmod => EventExec::event_chmod(status), ActionMap::ClearFlags => EventExec::event_clear_flags(status), + ActionMap::Command => EventExec::event_command(current_tab), + 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), @@ -107,10 +113,12 @@ impl ActionMap { ActionMap::Filter => EventExec::event_filter(current_tab), ActionMap::FlagAll => EventExec::event_flag_all(status), ActionMap::FuzzyFind => EventExec::event_fuzzyfind(status), + ActionMap::GitRoot => EventExec::event_git_root(current_tab), ActionMap::Goto => EventExec::event_goto(current_tab), ActionMap::Help => EventExec::event_help(status), ActionMap::History => EventExec::event_history(current_tab), ActionMap::Home => EventExec::event_home(current_tab), + ActionMap::Lazygit => EventExec::event_lazygit(status), ActionMap::Jump => EventExec::event_jump(status), ActionMap::KeyHome => EventExec::event_key_home(status, colors), ActionMap::MarksJump => EventExec::event_marks_jump(status), @@ -149,8 +157,8 @@ impl ActionMap { ActionMap::TrashOpen => EventExec::event_trash_open(status), ActionMap::Tree => EventExec::event_tree(status, colors), ActionMap::TreeFold => EventExec::event_tree_fold(current_tab, colors), - ActionMap::TreeFoldAll => EventExec::event_tree_fold_all(status, colors), - ActionMap::TreeUnFoldAll => EventExec::event_tree_unfold_all(status, colors), + ActionMap::TreeFoldAll => EventExec::event_tree_fold_all(current_tab, colors), + ActionMap::TreeUnFoldAll => EventExec::event_tree_unfold_all(current_tab, colors), ActionMap::OpenConfig => EventExec::event_open_config(status), ActionMap::Nothing => Ok(()), diff --git a/src/completion.rs b/src/completion.rs index 6894bb8..a4733ae 100644 --- a/src/completion.rs +++ b/src/completion.rs @@ -1,5 +1,7 @@ use std::fs::{self, ReadDir}; +use strum::IntoEnumIterator; + use crate::fileinfo::PathContent; use crate::fm_error::FmResult; use crate::mode::Mode; @@ -17,6 +19,8 @@ pub enum InputCompleted { Search, /// Complete an executable name from $PATH Exec, + /// Command + Command, } /// Holds a `Vec<String>` of possible completions and an `usize` index @@ -158,6 +162,20 @@ impl Completion { Ok(()) } + pub fn command(&mut self, input_string: &str) -> FmResult<()> { + let proposals = crate::action_map::ActionMap::iter() + .filter(|command| { + command + .to_string() + .to_lowercase() + .contains(&input_string.to_lowercase()) + }) + .map(|command| command.to_string()) + .collect(); + self.update(proposals); + Ok(()) + } + fn find_completion_in_path( path: std::path::PathBuf, input_string: &str, diff --git a/src/compress.rs b/src/compress.rs index 7ba8559..ac3ca50 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -1,29 +1,155 @@ -use std::fs::File; -use std::path::Path; - -use compress_tools::*; - -use crate::fm_error::{FmError, FmResult}; - -/// Decompress a compressed file into its parent directory. -/// It may fail an return a `FmError` if the file has no parent, -/// which should be impossible. -/// It used `compress_tools` which is a wrapper around `libarchive`. -pub fn decompress(source: &Path) -> FmResult<()> { - let parent = source - .parent() - .ok_or_else(|| FmError::custom("decompress", "source should have a parent"))?; - let file = File::open(source)?; - Ok(uncompress_archive(&file, parent, Ownership::Preserve)?) +use std::fmt::Display; +use std::io::prelude::*; +use std::io::Write; + +use crate::fm_error::FmResult; +use crate::impl_selectable_content; +use flate2::write::{DeflateEncoder, GzEncoder, ZlibEncoder}; +use flate2::Compression; +use lzma::LzmaWriter; +use zip::write::FileOptions; + +/// Different kind of compression methods +#[derive(Debug)] +pub enum CompressionMethod { + ZIP, + DEFLATE, + GZ, + ZLIB, + LZMA, } -/// Returns a list of compressed files within the archive. -/// it may fail if the file can't be opened or if libarchive -/// can't read it. -pub fn list_files<P>(source: P) -> FmResult<Vec<String>> -where - P: AsRef<Path>, -{ - let file = File::open(source)?; - Ok(list_archive_files(file)?) +impl Display for CompressionMethod { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::ZIP => write!(f, "ZIP: archive.zip"), + Self::DEFLATE => write!(f, "DEFLATE: archive.tar.gz"), + Self::LZMA => write!(f, "LZMA: archive.tar.xz"), + Self::GZ => write!(f, "GZ: archive.tar.gz"), + Self::ZLIB => write!(f, "ZLIB: archive.tar.xz"), + } + } } +/// Holds a vector of CompressionMethod and a few methods to compress some files. +#[derive(Debug)] +pub struct Compresser { + content: Vec<CompressionMethod>, + pub index: usize, +} + +impl Default for Compresser { + fn default() -> Self { + Self { + content: vec![ + CompressionMethod::ZIP, + CompressionMethod::LZMA, + CompressionMethod::ZLIB, + CompressionMethod::GZ, + CompressionMethod::DEFLATE, + ], + index: 0, + } + } +} + +impl Compresser { + /// Archive the files with tar and compress them with the selected method. + /// The compression method is chosen by the user. + pub fn compress(&self, files: Vec<std::path::PathBuf>) -> FmResult<()> { + let Some(selected) = self.selected() else { return Ok(()) }; + match selected { + CompressionMethod::DEFLATE => Self::compress_deflate("archive.tar.gz", files), + CompressionMethod::GZ => Self::compress_gzip("archive.tar.gz", files), + CompressionMethod::ZLIB => Self::compress_zlib("archive.tar.xz", files), + CompressionMethod::ZIP => Self::compress_zip("archive.zip", files), + CompressionMethod::LZMA => Self::compress_lzma("archive.tar.xz", files), + } + } + + fn make_tar<W>(files: Vec<std::path::PathBuf>, mut archive: tar::Builder<W>) -> FmResult<()> + where + W: Write, + { + for file in files.iter() { + if file.is_dir() { + archive.append_dir_all(file, file)?; + } else { + archive.append_path(file)?; + } + } + Ok(()) + } + + fn compress_gzip(archive_name: &str, files: Vec<std::path::PathBuf>) -> FmResult<()> { + let compressed_file = std::fs::File::create(archive_name)?; + let mut encoder = GzEncoder::new(compressed_file, Compression::default()); + + // Create tar archive and compress files + Self::make_tar(files, tar::Builder::new(&mut encoder))?; + + // Finish Gzip file + encoder.finish()?; + + Ok(()) + } + + fn compress_deflate(archive_name: &str, files: Vec<std::path::PathBuf>) -> FmResult<()> { + let compressed_file = std::fs::File::create(archive_name)?; + let mut encoder = DeflateEncoder::new(compressed_file, Compression::default()); + + // Create tar archive and compress files + Self::make_tar(files, tar::Builder::new(&mut encoder))?; + + // Finish deflate file + encoder.finish()?; + + Ok(()) + } + + fn compress_zlib(archive_name: &str, files: Vec<std::path::PathBuf>) -> FmResult<()> { + let compressed_file = std::fs::File::create(archive_name)?; + let mut encoder = ZlibEncoder::new(compressed_file, Compression::default()); + + // Create tar archive and compress files + Self::make_tar(files, tar::Builder::new(&mut encoder))?; + + // Finish zlib file + encoder.finish()?; + + Ok(()) + } + + fn compress_lzma(archive_name: &str, files: Vec<std::path::PathBuf>) -> FmResult<()> { + let compressed_file = std::fs::File::create(archive_name)?; + let mut encoder = LzmaWriter::new_compressor(compressed_file, 6)?; + + // Create tar archive and compress files + Self::make_tar(files, tar::Builder::new(&mut encoder))?; + + // Finish lzma file + encoder.finish()?; + + Ok(()) + } + + fn compress_zip(archive_name: &str, 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() { + zip.start_file( + file.to_str().unwrap(), + FileOptions::default().compression_method(zip::CompressionMethod::Bzip2), + )?; + let mut buffer = Vec::new(); + let mut content = std::fs::File::open(file)?; + content.read_to_end(&mut buffer)?; + zip.write_all(&buffer)?; + } + + // Finish zip file + zip.finish()?; + Ok(()) + } +} + +impl_selectable_content!(CompressionMethod, Compresser); diff --git a/src/decompress.rs b/src/decompress.rs new file mode 100644 in |