From 28a49a606f9f7460faf6d553997b70bc3a2b2332 Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Sun, 23 Nov 2025 14:58:05 -0800 Subject: [PATCH 01/20] Adding TarParam and TarOperation framework clippy and cargo fmt --- Cargo.lock | 104 ++++------------------ Cargo.toml | 1 - src/uu/tar/Cargo.toml | 1 + src/uu/tar/src/operations/create.rs | 22 ++++- src/uu/tar/src/operations/extract.rs | 16 ++++ src/uu/tar/src/operations/mod.rs | 6 ++ src/uu/tar/src/operations/operation.rs | 58 +++++++++++++ src/uu/tar/src/options/mod.rs | 6 ++ src/uu/tar/src/options/options.rs | 114 +++++++++++++++++++++++++ src/uu/tar/src/tar.rs | 44 ++-------- 10 files changed, 243 insertions(+), 129 deletions(-) create mode 100644 src/uu/tar/src/operations/operation.rs create mode 100644 src/uu/tar/src/options/mod.rs create mode 100644 src/uu/tar/src/options/options.rs diff --git a/Cargo.lock b/Cargo.lock index 86cd22d..47dbf29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -614,24 +614,24 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", - "serde", - "windows-sys 0.59.0", + "serde_core", + "windows-sys 0.61.2", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", @@ -1380,6 +1380,7 @@ name = "uu_tar" version = "0.0.1" dependencies = [ "clap", + "jiff", "regex", "tar", "uucore", @@ -1573,22 +1574,13 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -1600,22 +1592,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - [[package]] name = "windows-targets" version = "0.53.5" @@ -1623,106 +1599,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - [[package]] name = "windows_aarch64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - [[package]] name = "windows_aarch64_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - [[package]] name = "windows_i686_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - [[package]] name = "windows_i686_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - [[package]] name = "windows_i686_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - [[package]] name = "windows_x86_64_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - [[package]] name = "windows_x86_64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "windows_x86_64_msvc" version = "0.53.1" diff --git a/Cargo.toml b/Cargo.toml index 0d993e7..260cf94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,6 @@ uutests = { workspace = true } uucore = { workspace = true } uuhelp_parser = { workspace = true, optional = true } zip = { workspace = true, optional = true } - tar = { optional = true, version = "0.0.1", package = "uu_tar", path = "src/uu/tar" } [dev-dependencies] diff --git a/src/uu/tar/Cargo.toml b/src/uu/tar/Cargo.toml index d6ed134..a814f6f 100644 --- a/src/uu/tar/Cargo.toml +++ b/src/uu/tar/Cargo.toml @@ -17,6 +17,7 @@ uucore = { workspace = true } clap = { workspace = true } regex = { workspace = true } tar = { workspace = true } +jiff = "0.2.16" [lib] path = "src/tar.rs" diff --git a/src/uu/tar/src/operations/create.rs b/src/uu/tar/src/operations/create.rs index 022cb62..3e75aea 100644 --- a/src/uu/tar/src/operations/create.rs +++ b/src/uu/tar/src/operations/create.rs @@ -4,11 +4,29 @@ // file that was distributed with this source code. use crate::errors::TarError; +use crate::operations::TarOperation; +use crate::options::{TarOption, TarParams}; use std::fs::File; use std::path::Path; +use std::path::PathBuf; use tar::Builder; use uucore::error::UResult; +pub struct Create; + +impl TarOperation for Create { + fn exec(&self, options: &TarParams) -> UResult<()> { + create_archive( + options.archive(), + options.files().as_slice(), + options + .options() + .iter() + .any(|x| matches!(x, TarOption::Verbose)), + ) + } +} + /// Create a tar archive from the specified files /// /// # Arguments @@ -23,7 +41,7 @@ use uucore::error::UResult; /// - The archive file cannot be created /// - Any input file cannot be read /// - Files cannot be added due to I/O or permission errors -pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UResult<()> { +pub fn create_archive(archive_path: &Path, files: &[PathBuf], verbose: bool) -> UResult<()> { // Create the output file let file = File::create(archive_path).map_err(|e| { TarError::TarOperationError(format!( @@ -41,7 +59,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR } // Add each file or directory to the archive - for &path in files { + for path in files { if verbose { println!("{}", path.display()); } diff --git a/src/uu/tar/src/operations/extract.rs b/src/uu/tar/src/operations/extract.rs index 2668009..c283d93 100644 --- a/src/uu/tar/src/operations/extract.rs +++ b/src/uu/tar/src/operations/extract.rs @@ -4,11 +4,27 @@ // file that was distributed with this source code. use crate::errors::TarError; +use crate::operations::operation::TarOperation; +use crate::options::options::{TarOption, TarParams}; use std::fs::File; use std::path::Path; use tar::Archive; use uucore::error::UResult; +pub(crate) struct Extract; + +impl TarOperation for Extract { + fn exec(&self, options: &TarParams) -> UResult<()> { + extract_archive( + options.archive(), + options + .options() + .iter() + .any(|x| matches!(x, TarOption::Verbose)), + ) + } +} + /// Extract files from a tar archive /// /// # Arguments diff --git a/src/uu/tar/src/operations/mod.rs b/src/uu/tar/src/operations/mod.rs index b11efb7..911ab1f 100644 --- a/src/uu/tar/src/operations/mod.rs +++ b/src/uu/tar/src/operations/mod.rs @@ -5,3 +5,9 @@ pub mod create; pub mod extract; +pub mod operation; + +pub(crate) use self::create::Create; +pub(crate) use self::extract::Extract; +pub(crate) use self::operation::OperationKind; +pub use self::operation::TarOperation; diff --git a/src/uu/tar/src/operations/operation.rs b/src/uu/tar/src/operations/operation.rs new file mode 100644 index 0000000..e66042e --- /dev/null +++ b/src/uu/tar/src/operations/operation.rs @@ -0,0 +1,58 @@ +use crate::errors::TarError; +use crate::operations::Create; +use crate::operations::Extract; +use crate::options::TarParams; +use uucore::error::UResult; + +/// The [`OperationKind`] Enum representation of Acdtrux arguments which is +/// later leveraged as selector for enum dispatch by the [`TarOperation`] +/// trait +pub enum OperationKind { + Concatenate, + Create, + Diff, + List, + Append, + Update, + Extract, +} + +impl TryFrom<&str> for OperationKind { + type Error = TarError; + fn try_from(value: &str) -> Result { + match value { + "concate" => Ok(Self::Concatenate), + "create" => Ok(Self::Create), + "diff" => Ok(Self::Diff), + "list" => Ok(Self::List), + "append" => Ok(Self::Append), + "update" => Ok(Self::Update), + "extract" => Ok(Self::Extract), + _ => Err(TarError::TarOperationError(format!( + "Invalid operation selected: {}", + value + ))), + } + } +} + +impl TarOperation for OperationKind { + fn exec(&self, options: &TarParams) -> UResult<()> { + match self { + Self::List => unimplemented!(), + Self::Create => Create.exec(options), + Self::Diff => unimplemented!(), + Self::Append => unimplemented!(), + Self::Update => unimplemented!(), + Self::Extract => Extract.exec(options), + Self::Concatenate => unimplemented!(), + } + } +} + +/// [`TarOperation`] allows enum dispatch by enforcing the impl of the +/// trait to create the functionality to perform the operation requested via +/// the command line arg for this execution of tar +pub trait TarOperation { + fn exec(&self, options: &TarParams) -> UResult<()>; +} diff --git a/src/uu/tar/src/options/mod.rs b/src/uu/tar/src/options/mod.rs new file mode 100644 index 0000000..af8caf6 --- /dev/null +++ b/src/uu/tar/src/options/mod.rs @@ -0,0 +1,6 @@ +// re-exported to remove the redundant level of inception in +// the module tree +#[allow(clippy::module_inception)] +pub mod options; +pub use crate::options::options::TarOption; +pub use crate::options::options::TarParams; diff --git a/src/uu/tar/src/options/options.rs b/src/uu/tar/src/options/options.rs new file mode 100644 index 0000000..25a52dc --- /dev/null +++ b/src/uu/tar/src/options/options.rs @@ -0,0 +1,114 @@ +use crate::errors::TarError; +use crate::operations::OperationKind; +use clap::ArgMatches; +use std::path::PathBuf; +use uucore::error::UResult; + +/// [`TarParams`] Holds common information that is parsed from +/// command line arguments. That changes the current execution of +/// tar. +#[allow(dead_code)] +pub struct TarParams { + archive: PathBuf, + files: Vec, + options: Vec, +} + +/// [`Default`] Produces safe default values for [`TarParams`] and [`TarOption`]s +/// for this tar execution. Block-Size of 512 bytes, Empty vec's of +/// options and file names. +impl Default for TarParams { + fn default() -> TarParams { + Self { + archive: PathBuf::default(), + options: Vec::new(), + files: Vec::new(), + } + } +} + +// NOTE: I feel like this is just reimplmenting the parsing functionality of +// clap +impl From<&ArgMatches> for TarParams { + fn from(matches: &ArgMatches) -> TarParams { + let mut fp = vec![]; + let mut ops = Self::default(); + for i in matches.ids() { + match i.as_str() { + "verbose" => { + if matches.get_flag(i.as_str()) { + ops.options_mut().push(TarOption::Verbose); + } + } + "files" => { + if let Some(files) = matches.get_many::(i.as_str()) { + for file in files { + fp.push(file.to_owned()); + } + } + ops.files_mut().append(&mut fp); + } + "file" => { + if let Some(a) = matches.get_one::(i.as_str()) { + ops.archive = a.to_owned(); + } + } + _ => {} + } + } + ops + } +} + +impl TarParams { + /// Convence method that parses the [`ArgMatches`] + /// processed by clap into [`TarParams`] and selects + /// the appropriate [`OperationKind`] for execution given back to the caller in a + /// tuple of ([`OperationKind`], [`TarParams`]) + pub fn with_operation(matches: &ArgMatches) -> UResult<(OperationKind, Self)> { + if matches.get_flag("create") { + Ok((OperationKind::Create, Self::from(matches))) + } else if matches.get_flag("extract") { + Ok((OperationKind::Extract, Self::from(matches))) + } else { + // TODO: update messaging + Err(Box::new(TarError::TarOperationError( + "Error processing: Operations".to_string(), + ))) + } + } +} + +#[allow(dead_code)] +impl TarParams { + pub fn files(&self) -> &Vec { + &self.files + } + pub fn files_mut(&mut self) -> &mut Vec { + &mut self.files + } + pub fn archive(&self) -> &PathBuf { + &self.archive + } + pub fn archive_mut(&mut self) -> &mut PathBuf { + &mut self.archive + } + pub fn options(&self) -> &Vec { + &self.options + } + pub fn options_mut(&mut self) -> &mut Vec { + &mut self.options + } +} + +/// [`TarOption`] Enum of avaliable tar options for later use +/// by [`TarOperation`] impls, eg. List, Create, Delete +#[allow(dead_code)] +pub enum TarOption { + AbsoluteNames, + ACLs, + AfterDate, + Anchored, + AtimePreserve { arg: String }, + Verbose, +} diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 4db1bd5..455d883 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -5,9 +5,12 @@ pub mod errors; mod operations; +mod options; use clap::{arg, crate_version, ArgAction, Command}; -use std::path::{Path, PathBuf}; +use operations::operation::TarOperation; +use options::TarParams; +use std::path::PathBuf; use uucore::error::UResult; use uucore::format_usage; @@ -33,45 +36,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args_to_parse)?; - let verbose = matches.get_flag("verbose"); + let (op, options) = TarParams::with_operation(&matches)?; - // Handle extract operation - if matches.get_flag("extract") { - let archive_path = matches.get_one::("file").ok_or_else(|| { - uucore::error::USimpleError::new(1, "option requires an argument -- 'f'") - })?; - - return operations::extract::extract_archive(archive_path, verbose); - } - - // Handle create operation - if matches.get_flag("create") { - let archive_path = matches.get_one::("file").ok_or_else(|| { - uucore::error::USimpleError::new(1, "option requires an argument -- 'f'") - })?; - - let files: Vec<&Path> = matches - .get_many::("files") - .map(|v| v.map(|p| p.as_path()).collect()) - .unwrap_or_default(); - - if files.is_empty() { - return Err(uucore::error::USimpleError::new( - 1, - "Cowardly refusing to create an empty archive", - )); - } - - return operations::create::create_archive(archive_path, &files, verbose); - } - - // If no operation specified, show error - Err(uucore::error::USimpleError::new( - 1, - "You must specify one of the '-c' or '-x' options", - )) + op.exec(&options) } - #[allow(clippy::cognitive_complexity)] pub fn uu_app() -> Command { Command::new(uucore::util_name()) From 48699fb3202c5a39a2ba8c219fbcf01e4465b7a0 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Sun, 14 Dec 2025 16:35:05 +0100 Subject: [PATCH 02/20] Bump jiff from 0.2.15 to 0.2.16 --- Cargo.lock | 121 +++++++++++------------------------------------------ 1 file changed, 24 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a835bb..5fa49fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,7 +73,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.60.2", + "windows-sys", ] [[package]] @@ -84,7 +84,7 @@ checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys", ] [[package]] @@ -364,7 +364,7 @@ dependencies = [ "cfg-if", "libc", "socket2", - "windows-sys 0.60.2", + "windows-sys", ] [[package]] @@ -395,7 +395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -413,7 +413,7 @@ dependencies = [ "cfg-if", "libc", "libredox", - "windows-sys 0.60.2", + "windows-sys", ] [[package]] @@ -597,24 +597,24 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", - "serde", - "windows-sys 0.59.0", + "serde_core", + "windows-sys", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", @@ -1006,7 +1006,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -1110,7 +1110,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys", ] [[package]] @@ -1184,7 +1184,7 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -1194,7 +1194,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys", ] [[package]] @@ -1525,38 +1525,13 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows-targets", ] [[package]] @@ -1566,106 +1541,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - [[package]] name = "windows_aarch64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - [[package]] name = "windows_aarch64_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - [[package]] name = "windows_i686_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - [[package]] name = "windows_i686_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - [[package]] name = "windows_i686_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - [[package]] name = "windows_x86_64_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - [[package]] name = "windows_x86_64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "windows_x86_64_msvc" version = "0.53.1" From c156fe1df27b3d74eb099e454aa560c128cdd6aa Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Mon, 15 Dec 2025 09:49:46 -0800 Subject: [PATCH 03/20] changing arg match taroption processing Removing for match loop going over all matched args. Moved to standard if get_* for specific matches. --- src/uu/tar/src/options/options.rs | 44 +++++++++++-------------------- src/uu/tar/src/tar.rs | 1 + 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/uu/tar/src/options/options.rs b/src/uu/tar/src/options/options.rs index 25a52dc..eb58770 100644 --- a/src/uu/tar/src/options/options.rs +++ b/src/uu/tar/src/options/options.rs @@ -14,9 +14,6 @@ pub struct TarParams { options: Vec, } -/// [`Default`] Produces safe default values for [`TarParams`] and [`TarOption`]s -/// for this tar execution. Block-Size of 512 bytes, Empty vec's of -/// options and file names. impl Default for TarParams { fn default() -> TarParams { Self { @@ -27,35 +24,26 @@ impl Default for TarParams { } } -// NOTE: I feel like this is just reimplmenting the parsing functionality of -// clap impl From<&ArgMatches> for TarParams { fn from(matches: &ArgMatches) -> TarParams { - let mut fp = vec![]; + let mut ops = Self::default(); - for i in matches.ids() { - match i.as_str() { - "verbose" => { - if matches.get_flag(i.as_str()) { - ops.options_mut().push(TarOption::Verbose); - } - } - "files" => { - if let Some(files) = matches.get_many::(i.as_str()) { - for file in files { - fp.push(file.to_owned()); - } - } - ops.files_mut().append(&mut fp); - } - "file" => { - if let Some(a) = matches.get_one::(i.as_str()) { - ops.archive = a.to_owned(); - } - } - _ => {} - } + + // -v --verbose + if matches.get_flag("verbose") { + ops.options_mut().push(TarOption::Verbose); + } + + // [FILES]... + if let Some(files) = matches.get_many::("files") { + ops.files = files.map(|x| x.to_owned()).collect(); } + + // -f --file + if let Some(a) = matches.get_one::("file") { + ops.archive = a.to_owned(); + } + ops } } diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 455d883..b7d14bc 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -40,6 +40,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { op.exec(&options) } + #[allow(clippy::cognitive_complexity)] pub fn uu_app() -> Command { Command::new(uucore::util_name()) From 7c4cd013ad818d178e56f2375dcdde916743463f Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Tue, 16 Dec 2025 11:45:36 +0100 Subject: [PATCH 04/20] Remove "creating archive" output --- src/uu/tar/src/operations/create.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/uu/tar/src/operations/create.rs b/src/uu/tar/src/operations/create.rs index 4b296f8..f01ebe6 100644 --- a/src/uu/tar/src/operations/create.rs +++ b/src/uu/tar/src/operations/create.rs @@ -37,10 +37,6 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR // Create Builder instance let mut builder = Builder::new(file); - if verbose { - println!("Creating archive: {}", archive_path.display()); - } - // Add each file or directory to the archive for &path in files { // Check if path exists From 5223477380c0ef29180afa51e9ee8e4257bc3558 Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Tue, 16 Dec 2025 08:58:50 -0800 Subject: [PATCH 05/20] fmt and derive default tarparams --- src/uu/tar/src/options/options.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/uu/tar/src/options/options.rs b/src/uu/tar/src/options/options.rs index eb58770..8f4ab59 100644 --- a/src/uu/tar/src/options/options.rs +++ b/src/uu/tar/src/options/options.rs @@ -8,25 +8,15 @@ use uucore::error::UResult; /// command line arguments. That changes the current execution of /// tar. #[allow(dead_code)] +#[derive(Default)] pub struct TarParams { archive: PathBuf, files: Vec, options: Vec, } -impl Default for TarParams { - fn default() -> TarParams { - Self { - archive: PathBuf::default(), - options: Vec::new(), - files: Vec::new(), - } - } -} - impl From<&ArgMatches> for TarParams { fn from(matches: &ArgMatches) -> TarParams { - let mut ops = Self::default(); // -v --verbose From 6d0213b1c86d8ebba687315ca6cd8abf8e58bc77 Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Tue, 16 Dec 2025 09:16:38 -0800 Subject: [PATCH 06/20] merge for create verbose --- src/uu/tar/src/operations/create.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/uu/tar/src/operations/create.rs b/src/uu/tar/src/operations/create.rs index e728f6a..6e50bf5 100644 --- a/src/uu/tar/src/operations/create.rs +++ b/src/uu/tar/src/operations/create.rs @@ -6,10 +6,9 @@ use crate::errors::TarError; use crate::operations::TarOperation; use crate::options::{TarOption, TarParams}; -use std::fs::File; -use std::path::Path; -use std::path::PathBuf; use std::collections::VecDeque; +use std::fs::{self, File}; +use std::path::{self, Path, PathBuf}; use tar::Builder; use uucore::error::UResult; @@ -60,7 +59,7 @@ pub fn create_archive(archive_path: &Path, files: &[PathBuf], verbose: bool) -> } // Add each file or directory to the archive - for &path in files { + for path in files { // Check if path exists if !path.exists() { return Err(TarError::FileNotFound(path.display().to_string()).into()); From 982872a8e7a0685c0f159a41fcbc5101cde7ceef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 18:52:49 +0000 Subject: [PATCH 07/20] chore(deps): update rust crate console to v0.16.2 --- fuzz/Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index c6896e1..b86e8d8 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -183,9 +183,9 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "console" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" dependencies = [ "encode_unicode", "libc", @@ -780,7 +780,7 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.61.0", + "windows-sys 0.59.0", ] [[package]] From eab3aa42a214d47a56ca2e7e1d521b45e51b1d05 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Dec 2025 17:30:26 +0000 Subject: [PATCH 08/20] chore(deps): update rust crate clap_complete to v4.5.62 --- Cargo.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a835bb..c00500f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.61" +version = "4.5.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39615915e2ece2550c0149addac32fb5bd312c657f43845bb9088cb9c8a7c992" +checksum = "004eef6b14ce34759aa7de4aea3217e368f463f46a3ed3764ca4b5a4404003b4" dependencies = [ "clap", ] @@ -395,7 +395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1006,7 +1006,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1184,7 +1184,7 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] From 76240d05f723a291b0da3d03a5300a30d5977211 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 20 Dec 2025 01:39:40 +0000 Subject: [PATCH 09/20] chore(deps): update rust crate zip to v7 --- Cargo.lock | 13 +++++++------ Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c00500f..772384b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -480,9 +480,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -702,9 +702,9 @@ checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "lzma-rust2" -version = "0.13.0" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c60a23ffb90d527e23192f1246b14746e2f7f071cb84476dd879071696c18a4a" +checksum = "48172246aa7c3ea28e423295dd1ca2589a24617cc4e588bb8cfe177cb2c54d95" dependencies = [ "crc", "sha2", @@ -1752,9 +1752,9 @@ dependencies = [ [[package]] name = "zip" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b" +checksum = "bdd8a47718a4ee5fe78e07667cd36f3de80e7c2bfe727c7074245ffc7303c037" dependencies = [ "aes", "arbitrary", @@ -1763,6 +1763,7 @@ dependencies = [ "crc32fast", "deflate64", "flate2", + "generic-array", "getrandom", "hmac", "indexmap", diff --git a/Cargo.toml b/Cargo.toml index 7651872..65e190f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ tar = "0.4" tempfile = "3.10.1" textwrap = { version = "0.16.1", features = ["terminal_size"] } xattr = "1.3.1" -zip = "6.0" +zip = "7.0" [dependencies] clap = { workspace = true } From 6b686b9f8fa9b2f82e3490afec1f6fbd0e673bd9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 24 Dec 2025 01:48:24 +0000 Subject: [PATCH 10/20] chore(deps): update rust crate tempfile to v3.24.0 --- Cargo.lock | 14 +++++++------- fuzz/Cargo.lock | 16 ++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 772384b..2236893 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -395,7 +395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -998,15 +998,15 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -1176,15 +1176,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index b86e8d8..4ef90d5 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -437,9 +437,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "log" @@ -669,15 +669,15 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -772,15 +772,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" dependencies = [ "fastrand", "getrandom", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] From 9f23bf23d66c3e860d034aee5be4d2bce3bc15bb Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Mon, 29 Dec 2025 14:37:44 -0800 Subject: [PATCH 11/20] cargo workspace dependency fixes --- Cargo.toml | 3 +++ src/uu/tar/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 6a703f7..64458e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ tempfile = "3.10.1" textwrap = { version = "0.16.1", features = ["terminal_size"] } xattr = "1.3.1" zip = "6.0" +jiff = "0.2.16" [dependencies] clap = { workspace = true } @@ -64,6 +65,8 @@ uucore = { workspace = true } uuhelp_parser = { workspace = true, optional = true } zip = { workspace = true, optional = true } tar = { optional = true, version = "0.0.1", package = "uu_tar", path = "src/uu/tar" } +jiff = { workspace = true } + [dev-dependencies] chrono = { workspace = true } diff --git a/src/uu/tar/Cargo.toml b/src/uu/tar/Cargo.toml index a814f6f..ebb06ab 100644 --- a/src/uu/tar/Cargo.toml +++ b/src/uu/tar/Cargo.toml @@ -17,7 +17,7 @@ uucore = { workspace = true } clap = { workspace = true } regex = { workspace = true } tar = { workspace = true } -jiff = "0.2.16" +jiff = { workspace = true } [lib] path = "src/tar.rs" From 818e1bd86c4ac8e15ab265ba316e925099013537 Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Sun, 23 Nov 2025 14:58:05 -0800 Subject: [PATCH 12/20] Adding TarParam and TarOperation framework clippy and cargo fmt --- Cargo.lock | 1 + Cargo.toml | 1 - src/uu/tar/Cargo.toml | 1 + src/uu/tar/src/operations/create.rs | 21 ++++- src/uu/tar/src/operations/extract.rs | 16 ++++ src/uu/tar/src/operations/mod.rs | 6 ++ src/uu/tar/src/operations/operation.rs | 58 +++++++++++++ src/uu/tar/src/options/mod.rs | 6 ++ src/uu/tar/src/options/options.rs | 114 +++++++++++++++++++++++++ src/uu/tar/src/tar.rs | 44 ++-------- 10 files changed, 227 insertions(+), 41 deletions(-) create mode 100644 src/uu/tar/src/operations/operation.rs create mode 100644 src/uu/tar/src/options/mod.rs create mode 100644 src/uu/tar/src/options/options.rs diff --git a/Cargo.lock b/Cargo.lock index 3f66cb2..a877db6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1335,6 +1335,7 @@ name = "uu_tar" version = "0.0.1" dependencies = [ "clap", + "jiff", "regex", "tar", "uucore", diff --git a/Cargo.toml b/Cargo.toml index 65e190f..e219663 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,6 @@ uutests = { workspace = true } uucore = { workspace = true } uuhelp_parser = { workspace = true, optional = true } zip = { workspace = true, optional = true } - tar = { optional = true, version = "0.0.1", package = "uu_tar", path = "src/uu/tar" } [dev-dependencies] diff --git a/src/uu/tar/Cargo.toml b/src/uu/tar/Cargo.toml index d6ed134..a814f6f 100644 --- a/src/uu/tar/Cargo.toml +++ b/src/uu/tar/Cargo.toml @@ -17,6 +17,7 @@ uucore = { workspace = true } clap = { workspace = true } regex = { workspace = true } tar = { workspace = true } +jiff = "0.2.16" [lib] path = "src/tar.rs" diff --git a/src/uu/tar/src/operations/create.rs b/src/uu/tar/src/operations/create.rs index f01ebe6..d8caa27 100644 --- a/src/uu/tar/src/operations/create.rs +++ b/src/uu/tar/src/operations/create.rs @@ -4,12 +4,29 @@ // file that was distributed with this source code. use crate::errors::TarError; +use crate::operations::TarOperation; +use crate::options::{TarOption, TarParams}; use std::collections::VecDeque; use std::fs::{self, File}; use std::path::{self, Path, PathBuf}; use tar::Builder; use uucore::error::UResult; +pub struct Create; + +impl TarOperation for Create { + fn exec(&self, options: &TarParams) -> UResult<()> { + create_archive( + options.archive(), + options.files().as_slice(), + options + .options() + .iter() + .any(|x| matches!(x, TarOption::Verbose)), + ) + } +} + /// Create a tar archive from the specified files /// /// # Arguments @@ -24,7 +41,7 @@ use uucore::error::UResult; /// - The archive file cannot be created /// - Any input file cannot be read /// - Files cannot be added due to I/O or permission errors -pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UResult<()> { +pub fn create_archive(archive_path: &Path, files: &[PathBuf], verbose: bool) -> UResult<()> { // Create the output file let file = File::create(archive_path).map_err(|e| { TarError::TarOperationError(format!( @@ -38,7 +55,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR let mut builder = Builder::new(file); // Add each file or directory to the archive - for &path in files { + for path in files { // Check if path exists if !path.exists() { return Err(TarError::FileNotFound(path.display().to_string()).into()); diff --git a/src/uu/tar/src/operations/extract.rs b/src/uu/tar/src/operations/extract.rs index 2668009..c283d93 100644 --- a/src/uu/tar/src/operations/extract.rs +++ b/src/uu/tar/src/operations/extract.rs @@ -4,11 +4,27 @@ // file that was distributed with this source code. use crate::errors::TarError; +use crate::operations::operation::TarOperation; +use crate::options::options::{TarOption, TarParams}; use std::fs::File; use std::path::Path; use tar::Archive; use uucore::error::UResult; +pub(crate) struct Extract; + +impl TarOperation for Extract { + fn exec(&self, options: &TarParams) -> UResult<()> { + extract_archive( + options.archive(), + options + .options() + .iter() + .any(|x| matches!(x, TarOption::Verbose)), + ) + } +} + /// Extract files from a tar archive /// /// # Arguments diff --git a/src/uu/tar/src/operations/mod.rs b/src/uu/tar/src/operations/mod.rs index b11efb7..911ab1f 100644 --- a/src/uu/tar/src/operations/mod.rs +++ b/src/uu/tar/src/operations/mod.rs @@ -5,3 +5,9 @@ pub mod create; pub mod extract; +pub mod operation; + +pub(crate) use self::create::Create; +pub(crate) use self::extract::Extract; +pub(crate) use self::operation::OperationKind; +pub use self::operation::TarOperation; diff --git a/src/uu/tar/src/operations/operation.rs b/src/uu/tar/src/operations/operation.rs new file mode 100644 index 0000000..e66042e --- /dev/null +++ b/src/uu/tar/src/operations/operation.rs @@ -0,0 +1,58 @@ +use crate::errors::TarError; +use crate::operations::Create; +use crate::operations::Extract; +use crate::options::TarParams; +use uucore::error::UResult; + +/// The [`OperationKind`] Enum representation of Acdtrux arguments which is +/// later leveraged as selector for enum dispatch by the [`TarOperation`] +/// trait +pub enum OperationKind { + Concatenate, + Create, + Diff, + List, + Append, + Update, + Extract, +} + +impl TryFrom<&str> for OperationKind { + type Error = TarError; + fn try_from(value: &str) -> Result { + match value { + "concate" => Ok(Self::Concatenate), + "create" => Ok(Self::Create), + "diff" => Ok(Self::Diff), + "list" => Ok(Self::List), + "append" => Ok(Self::Append), + "update" => Ok(Self::Update), + "extract" => Ok(Self::Extract), + _ => Err(TarError::TarOperationError(format!( + "Invalid operation selected: {}", + value + ))), + } + } +} + +impl TarOperation for OperationKind { + fn exec(&self, options: &TarParams) -> UResult<()> { + match self { + Self::List => unimplemented!(), + Self::Create => Create.exec(options), + Self::Diff => unimplemented!(), + Self::Append => unimplemented!(), + Self::Update => unimplemented!(), + Self::Extract => Extract.exec(options), + Self::Concatenate => unimplemented!(), + } + } +} + +/// [`TarOperation`] allows enum dispatch by enforcing the impl of the +/// trait to create the functionality to perform the operation requested via +/// the command line arg for this execution of tar +pub trait TarOperation { + fn exec(&self, options: &TarParams) -> UResult<()>; +} diff --git a/src/uu/tar/src/options/mod.rs b/src/uu/tar/src/options/mod.rs new file mode 100644 index 0000000..af8caf6 --- /dev/null +++ b/src/uu/tar/src/options/mod.rs @@ -0,0 +1,6 @@ +// re-exported to remove the redundant level of inception in +// the module tree +#[allow(clippy::module_inception)] +pub mod options; +pub use crate::options::options::TarOption; +pub use crate::options::options::TarParams; diff --git a/src/uu/tar/src/options/options.rs b/src/uu/tar/src/options/options.rs new file mode 100644 index 0000000..25a52dc --- /dev/null +++ b/src/uu/tar/src/options/options.rs @@ -0,0 +1,114 @@ +use crate::errors::TarError; +use crate::operations::OperationKind; +use clap::ArgMatches; +use std::path::PathBuf; +use uucore::error::UResult; + +/// [`TarParams`] Holds common information that is parsed from +/// command line arguments. That changes the current execution of +/// tar. +#[allow(dead_code)] +pub struct TarParams { + archive: PathBuf, + files: Vec, + options: Vec, +} + +/// [`Default`] Produces safe default values for [`TarParams`] and [`TarOption`]s +/// for this tar execution. Block-Size of 512 bytes, Empty vec's of +/// options and file names. +impl Default for TarParams { + fn default() -> TarParams { + Self { + archive: PathBuf::default(), + options: Vec::new(), + files: Vec::new(), + } + } +} + +// NOTE: I feel like this is just reimplmenting the parsing functionality of +// clap +impl From<&ArgMatches> for TarParams { + fn from(matches: &ArgMatches) -> TarParams { + let mut fp = vec![]; + let mut ops = Self::default(); + for i in matches.ids() { + match i.as_str() { + "verbose" => { + if matches.get_flag(i.as_str()) { + ops.options_mut().push(TarOption::Verbose); + } + } + "files" => { + if let Some(files) = matches.get_many::(i.as_str()) { + for file in files { + fp.push(file.to_owned()); + } + } + ops.files_mut().append(&mut fp); + } + "file" => { + if let Some(a) = matches.get_one::(i.as_str()) { + ops.archive = a.to_owned(); + } + } + _ => {} + } + } + ops + } +} + +impl TarParams { + /// Convence method that parses the [`ArgMatches`] + /// processed by clap into [`TarParams`] and selects + /// the appropriate [`OperationKind`] for execution given back to the caller in a + /// tuple of ([`OperationKind`], [`TarParams`]) + pub fn with_operation(matches: &ArgMatches) -> UResult<(OperationKind, Self)> { + if matches.get_flag("create") { + Ok((OperationKind::Create, Self::from(matches))) + } else if matches.get_flag("extract") { + Ok((OperationKind::Extract, Self::from(matches))) + } else { + // TODO: update messaging + Err(Box::new(TarError::TarOperationError( + "Error processing: Operations".to_string(), + ))) + } + } +} + +#[allow(dead_code)] +impl TarParams { + pub fn files(&self) -> &Vec { + &self.files + } + pub fn files_mut(&mut self) -> &mut Vec { + &mut self.files + } + pub fn archive(&self) -> &PathBuf { + &self.archive + } + pub fn archive_mut(&mut self) -> &mut PathBuf { + &mut self.archive + } + pub fn options(&self) -> &Vec { + &self.options + } + pub fn options_mut(&mut self) -> &mut Vec { + &mut self.options + } +} + +/// [`TarOption`] Enum of avaliable tar options for later use +/// by [`TarOperation`] impls, eg. List, Create, Delete +#[allow(dead_code)] +pub enum TarOption { + AbsoluteNames, + ACLs, + AfterDate, + Anchored, + AtimePreserve { arg: String }, + Verbose, +} diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 4db1bd5..455d883 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -5,9 +5,12 @@ pub mod errors; mod operations; +mod options; use clap::{arg, crate_version, ArgAction, Command}; -use std::path::{Path, PathBuf}; +use operations::operation::TarOperation; +use options::TarParams; +use std::path::PathBuf; use uucore::error::UResult; use uucore::format_usage; @@ -33,45 +36,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args_to_parse)?; - let verbose = matches.get_flag("verbose"); + let (op, options) = TarParams::with_operation(&matches)?; - // Handle extract operation - if matches.get_flag("extract") { - let archive_path = matches.get_one::("file").ok_or_else(|| { - uucore::error::USimpleError::new(1, "option requires an argument -- 'f'") - })?; - - return operations::extract::extract_archive(archive_path, verbose); - } - - // Handle create operation - if matches.get_flag("create") { - let archive_path = matches.get_one::("file").ok_or_else(|| { - uucore::error::USimpleError::new(1, "option requires an argument -- 'f'") - })?; - - let files: Vec<&Path> = matches - .get_many::("files") - .map(|v| v.map(|p| p.as_path()).collect()) - .unwrap_or_default(); - - if files.is_empty() { - return Err(uucore::error::USimpleError::new( - 1, - "Cowardly refusing to create an empty archive", - )); - } - - return operations::create::create_archive(archive_path, &files, verbose); - } - - // If no operation specified, show error - Err(uucore::error::USimpleError::new( - 1, - "You must specify one of the '-c' or '-x' options", - )) + op.exec(&options) } - #[allow(clippy::cognitive_complexity)] pub fn uu_app() -> Command { Command::new(uucore::util_name()) From 4e43d5e08064fd0be8c8fc9478e9ec082fbcde1c Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Mon, 15 Dec 2025 09:49:46 -0800 Subject: [PATCH 13/20] changing arg match taroption processing Removing for match loop going over all matched args. Moved to standard if get_* for specific matches. --- src/uu/tar/src/options/options.rs | 44 +++++++++++-------------------- src/uu/tar/src/tar.rs | 1 + 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/uu/tar/src/options/options.rs b/src/uu/tar/src/options/options.rs index 25a52dc..eb58770 100644 --- a/src/uu/tar/src/options/options.rs +++ b/src/uu/tar/src/options/options.rs @@ -14,9 +14,6 @@ pub struct TarParams { options: Vec, } -/// [`Default`] Produces safe default values for [`TarParams`] and [`TarOption`]s -/// for this tar execution. Block-Size of 512 bytes, Empty vec's of -/// options and file names. impl Default for TarParams { fn default() -> TarParams { Self { @@ -27,35 +24,26 @@ impl Default for TarParams { } } -// NOTE: I feel like this is just reimplmenting the parsing functionality of -// clap impl From<&ArgMatches> for TarParams { fn from(matches: &ArgMatches) -> TarParams { - let mut fp = vec![]; + let mut ops = Self::default(); - for i in matches.ids() { - match i.as_str() { - "verbose" => { - if matches.get_flag(i.as_str()) { - ops.options_mut().push(TarOption::Verbose); - } - } - "files" => { - if let Some(files) = matches.get_many::(i.as_str()) { - for file in files { - fp.push(file.to_owned()); - } - } - ops.files_mut().append(&mut fp); - } - "file" => { - if let Some(a) = matches.get_one::(i.as_str()) { - ops.archive = a.to_owned(); - } - } - _ => {} - } + + // -v --verbose + if matches.get_flag("verbose") { + ops.options_mut().push(TarOption::Verbose); + } + + // [FILES]... + if let Some(files) = matches.get_many::("files") { + ops.files = files.map(|x| x.to_owned()).collect(); } + + // -f --file + if let Some(a) = matches.get_one::("file") { + ops.archive = a.to_owned(); + } + ops } } diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 455d883..b7d14bc 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -40,6 +40,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { op.exec(&options) } + #[allow(clippy::cognitive_complexity)] pub fn uu_app() -> Command { Command::new(uucore::util_name()) From e5a9b0dd0521286a10a803d1b0b2196dfe46371b Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Tue, 16 Dec 2025 08:58:50 -0800 Subject: [PATCH 14/20] fmt and derive default tarparams --- src/uu/tar/src/options/options.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/uu/tar/src/options/options.rs b/src/uu/tar/src/options/options.rs index eb58770..8f4ab59 100644 --- a/src/uu/tar/src/options/options.rs +++ b/src/uu/tar/src/options/options.rs @@ -8,25 +8,15 @@ use uucore::error::UResult; /// command line arguments. That changes the current execution of /// tar. #[allow(dead_code)] +#[derive(Default)] pub struct TarParams { archive: PathBuf, files: Vec, options: Vec, } -impl Default for TarParams { - fn default() -> TarParams { - Self { - archive: PathBuf::default(), - options: Vec::new(), - files: Vec::new(), - } - } -} - impl From<&ArgMatches> for TarParams { fn from(matches: &ArgMatches) -> TarParams { - let mut ops = Self::default(); // -v --verbose From 0e9970b1353c02efcbaa1d847f54220233c5d7e2 Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Mon, 29 Dec 2025 14:37:44 -0800 Subject: [PATCH 15/20] cargo workspace dependency fixes --- Cargo.toml | 3 +++ src/uu/tar/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e219663..9f6dd63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ tempfile = "3.10.1" textwrap = { version = "0.16.1", features = ["terminal_size"] } xattr = "1.3.1" zip = "7.0" +jiff = "0.2.16" [dependencies] clap = { workspace = true } @@ -64,6 +65,8 @@ uucore = { workspace = true } uuhelp_parser = { workspace = true, optional = true } zip = { workspace = true, optional = true } tar = { optional = true, version = "0.0.1", package = "uu_tar", path = "src/uu/tar" } +jiff = { workspace = true } + [dev-dependencies] chrono = { workspace = true } diff --git a/src/uu/tar/Cargo.toml b/src/uu/tar/Cargo.toml index a814f6f..ebb06ab 100644 --- a/src/uu/tar/Cargo.toml +++ b/src/uu/tar/Cargo.toml @@ -17,7 +17,7 @@ uucore = { workspace = true } clap = { workspace = true } regex = { workspace = true } tar = { workspace = true } -jiff = "0.2.16" +jiff = { workspace = true } [lib] path = "src/tar.rs" From b1b486f0ec7421e48ef9d3aecb5a9159dded70dd Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Sun, 23 Nov 2025 14:58:05 -0800 Subject: [PATCH 16/20] Adding TarParam and TarOperation framework clippy and cargo fmt --- src/uu/tar/src/tar.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index b7d14bc..455d883 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -40,7 +40,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { op.exec(&options) } - #[allow(clippy::cognitive_complexity)] pub fn uu_app() -> Command { Command::new(uucore::util_name()) From 4bef208161a1271385119982c29aa3d399ed90ff Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Mon, 15 Dec 2025 09:49:46 -0800 Subject: [PATCH 17/20] changing arg match taroption processing Removing for match loop going over all matched args. Moved to standard if get_* for specific matches. --- src/uu/tar/src/options/options.rs | 10 ++++++++++ src/uu/tar/src/tar.rs | 1 + 2 files changed, 11 insertions(+) diff --git a/src/uu/tar/src/options/options.rs b/src/uu/tar/src/options/options.rs index 8f4ab59..45370aa 100644 --- a/src/uu/tar/src/options/options.rs +++ b/src/uu/tar/src/options/options.rs @@ -15,6 +15,16 @@ pub struct TarParams { options: Vec, } +impl Default for TarParams { + fn default() -> TarParams { + Self { + archive: PathBuf::default(), + options: Vec::new(), + files: Vec::new(), + } + } +} + impl From<&ArgMatches> for TarParams { fn from(matches: &ArgMatches) -> TarParams { let mut ops = Self::default(); diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 455d883..b7d14bc 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -40,6 +40,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { op.exec(&options) } + #[allow(clippy::cognitive_complexity)] pub fn uu_app() -> Command { Command::new(uucore::util_name()) From c959d9031728780b87a0c26b26236fd0d90e5538 Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Mon, 29 Dec 2025 15:37:41 -0800 Subject: [PATCH 18/20] Revert "changing arg match taroption processing" This reverts commit 4bef208161a1271385119982c29aa3d399ed90ff. --- src/uu/tar/src/tar.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index b7d14bc..455d883 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -40,7 +40,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { op.exec(&options) } - #[allow(clippy::cognitive_complexity)] pub fn uu_app() -> Command { Command::new(uucore::util_name()) From d771c526740fa3a4cab0ec0e1419ec88e35b3b68 Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Mon, 29 Dec 2025 14:37:44 -0800 Subject: [PATCH 19/20] cargo workspace dependency fixes Adding TarParam and TarOperation framework clippy and cargo fmt changing arg match taroption processing Removing for match loop going over all matched args. Moved to standard if get_* for specific matches. fmt and derive default tarparams --- Cargo.toml | 2 +- src/uu/tar/src/options/options.rs | 10 ---------- src/uu/tar/src/tar.rs | 1 + 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 64458e1..9f6dd63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ tar = "0.4" tempfile = "3.10.1" textwrap = { version = "0.16.1", features = ["terminal_size"] } xattr = "1.3.1" -zip = "6.0" +zip = "7.0" jiff = "0.2.16" [dependencies] diff --git a/src/uu/tar/src/options/options.rs b/src/uu/tar/src/options/options.rs index 45370aa..8f4ab59 100644 --- a/src/uu/tar/src/options/options.rs +++ b/src/uu/tar/src/options/options.rs @@ -15,16 +15,6 @@ pub struct TarParams { options: Vec, } -impl Default for TarParams { - fn default() -> TarParams { - Self { - archive: PathBuf::default(), - options: Vec::new(), - files: Vec::new(), - } - } -} - impl From<&ArgMatches> for TarParams { fn from(matches: &ArgMatches) -> TarParams { let mut ops = Self::default(); diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 455d883..b7d14bc 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -40,6 +40,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { op.exec(&options) } + #[allow(clippy::cognitive_complexity)] pub fn uu_app() -> Command { Command::new(uucore::util_name()) From df20a722952949d4b6d0c1b46c0d07240582f8c2 Mon Sep 17 00:00:00 2001 From: Nicholas Still Date: Sun, 23 Nov 2025 14:58:05 -0800 Subject: [PATCH 20/20] Adding TarParam and TarOperation framework clippy and cargo fmt changing arg match taroption processing Removing for match loop going over all matched args. Moved to standard if get_* for specific matches. fmt and derive default tarparams cargo workspace dependency fixes Adding TarParam and TarOperation framework clippy and cargo fmt changing arg match taroption processing Removing for match loop going over all matched args. Moved to standard if get_* for specific matches. Revert "changing arg match taroption processing" This reverts commit 4bef208161a1271385119982c29aa3d399ed90ff. changing arg match taroption processing Removing for match loop going over all matched args. Moved to standard if get_* for specific matches. cargo workspace dependency fixes Adding TarParam and TarOperation framework clippy and cargo fmt changing arg match taroption processing Removing for match loop going over all matched args. Moved to standard if get_* for specific matches. fmt and derive default tarparams --- Cargo.lock | 1 + Cargo.toml | 4 +- src/uu/tar/Cargo.toml | 1 + src/uu/tar/src/operations/create.rs | 21 +++++- src/uu/tar/src/operations/extract.rs | 16 +++++ src/uu/tar/src/operations/mod.rs | 6 ++ src/uu/tar/src/operations/operation.rs | 58 ++++++++++++++++ src/uu/tar/src/options/mod.rs | 6 ++ src/uu/tar/src/options/options.rs | 92 ++++++++++++++++++++++++++ src/uu/tar/src/tar.rs | 43 ++---------- 10 files changed, 208 insertions(+), 40 deletions(-) create mode 100644 src/uu/tar/src/operations/operation.rs create mode 100644 src/uu/tar/src/options/mod.rs create mode 100644 src/uu/tar/src/options/options.rs diff --git a/Cargo.lock b/Cargo.lock index 3f66cb2..a877db6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1335,6 +1335,7 @@ name = "uu_tar" version = "0.0.1" dependencies = [ "clap", + "jiff", "regex", "tar", "uucore", diff --git a/Cargo.toml b/Cargo.toml index 65e190f..9f6dd63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ tempfile = "3.10.1" textwrap = { version = "0.16.1", features = ["terminal_size"] } xattr = "1.3.1" zip = "7.0" +jiff = "0.2.16" [dependencies] clap = { workspace = true } @@ -63,8 +64,9 @@ uutests = { workspace = true } uucore = { workspace = true } uuhelp_parser = { workspace = true, optional = true } zip = { workspace = true, optional = true } - tar = { optional = true, version = "0.0.1", package = "uu_tar", path = "src/uu/tar" } +jiff = { workspace = true } + [dev-dependencies] chrono = { workspace = true } diff --git a/src/uu/tar/Cargo.toml b/src/uu/tar/Cargo.toml index d6ed134..ebb06ab 100644 --- a/src/uu/tar/Cargo.toml +++ b/src/uu/tar/Cargo.toml @@ -17,6 +17,7 @@ uucore = { workspace = true } clap = { workspace = true } regex = { workspace = true } tar = { workspace = true } +jiff = { workspace = true } [lib] path = "src/tar.rs" diff --git a/src/uu/tar/src/operations/create.rs b/src/uu/tar/src/operations/create.rs index f01ebe6..d8caa27 100644 --- a/src/uu/tar/src/operations/create.rs +++ b/src/uu/tar/src/operations/create.rs @@ -4,12 +4,29 @@ // file that was distributed with this source code. use crate::errors::TarError; +use crate::operations::TarOperation; +use crate::options::{TarOption, TarParams}; use std::collections::VecDeque; use std::fs::{self, File}; use std::path::{self, Path, PathBuf}; use tar::Builder; use uucore::error::UResult; +pub struct Create; + +impl TarOperation for Create { + fn exec(&self, options: &TarParams) -> UResult<()> { + create_archive( + options.archive(), + options.files().as_slice(), + options + .options() + .iter() + .any(|x| matches!(x, TarOption::Verbose)), + ) + } +} + /// Create a tar archive from the specified files /// /// # Arguments @@ -24,7 +41,7 @@ use uucore::error::UResult; /// - The archive file cannot be created /// - Any input file cannot be read /// - Files cannot be added due to I/O or permission errors -pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UResult<()> { +pub fn create_archive(archive_path: &Path, files: &[PathBuf], verbose: bool) -> UResult<()> { // Create the output file let file = File::create(archive_path).map_err(|e| { TarError::TarOperationError(format!( @@ -38,7 +55,7 @@ pub fn create_archive(archive_path: &Path, files: &[&Path], verbose: bool) -> UR let mut builder = Builder::new(file); // Add each file or directory to the archive - for &path in files { + for path in files { // Check if path exists if !path.exists() { return Err(TarError::FileNotFound(path.display().to_string()).into()); diff --git a/src/uu/tar/src/operations/extract.rs b/src/uu/tar/src/operations/extract.rs index 2668009..c283d93 100644 --- a/src/uu/tar/src/operations/extract.rs +++ b/src/uu/tar/src/operations/extract.rs @@ -4,11 +4,27 @@ // file that was distributed with this source code. use crate::errors::TarError; +use crate::operations::operation::TarOperation; +use crate::options::options::{TarOption, TarParams}; use std::fs::File; use std::path::Path; use tar::Archive; use uucore::error::UResult; +pub(crate) struct Extract; + +impl TarOperation for Extract { + fn exec(&self, options: &TarParams) -> UResult<()> { + extract_archive( + options.archive(), + options + .options() + .iter() + .any(|x| matches!(x, TarOption::Verbose)), + ) + } +} + /// Extract files from a tar archive /// /// # Arguments diff --git a/src/uu/tar/src/operations/mod.rs b/src/uu/tar/src/operations/mod.rs index b11efb7..911ab1f 100644 --- a/src/uu/tar/src/operations/mod.rs +++ b/src/uu/tar/src/operations/mod.rs @@ -5,3 +5,9 @@ pub mod create; pub mod extract; +pub mod operation; + +pub(crate) use self::create::Create; +pub(crate) use self::extract::Extract; +pub(crate) use self::operation::OperationKind; +pub use self::operation::TarOperation; diff --git a/src/uu/tar/src/operations/operation.rs b/src/uu/tar/src/operations/operation.rs new file mode 100644 index 0000000..e66042e --- /dev/null +++ b/src/uu/tar/src/operations/operation.rs @@ -0,0 +1,58 @@ +use crate::errors::TarError; +use crate::operations::Create; +use crate::operations::Extract; +use crate::options::TarParams; +use uucore::error::UResult; + +/// The [`OperationKind`] Enum representation of Acdtrux arguments which is +/// later leveraged as selector for enum dispatch by the [`TarOperation`] +/// trait +pub enum OperationKind { + Concatenate, + Create, + Diff, + List, + Append, + Update, + Extract, +} + +impl TryFrom<&str> for OperationKind { + type Error = TarError; + fn try_from(value: &str) -> Result { + match value { + "concate" => Ok(Self::Concatenate), + "create" => Ok(Self::Create), + "diff" => Ok(Self::Diff), + "list" => Ok(Self::List), + "append" => Ok(Self::Append), + "update" => Ok(Self::Update), + "extract" => Ok(Self::Extract), + _ => Err(TarError::TarOperationError(format!( + "Invalid operation selected: {}", + value + ))), + } + } +} + +impl TarOperation for OperationKind { + fn exec(&self, options: &TarParams) -> UResult<()> { + match self { + Self::List => unimplemented!(), + Self::Create => Create.exec(options), + Self::Diff => unimplemented!(), + Self::Append => unimplemented!(), + Self::Update => unimplemented!(), + Self::Extract => Extract.exec(options), + Self::Concatenate => unimplemented!(), + } + } +} + +/// [`TarOperation`] allows enum dispatch by enforcing the impl of the +/// trait to create the functionality to perform the operation requested via +/// the command line arg for this execution of tar +pub trait TarOperation { + fn exec(&self, options: &TarParams) -> UResult<()>; +} diff --git a/src/uu/tar/src/options/mod.rs b/src/uu/tar/src/options/mod.rs new file mode 100644 index 0000000..af8caf6 --- /dev/null +++ b/src/uu/tar/src/options/mod.rs @@ -0,0 +1,6 @@ +// re-exported to remove the redundant level of inception in +// the module tree +#[allow(clippy::module_inception)] +pub mod options; +pub use crate::options::options::TarOption; +pub use crate::options::options::TarParams; diff --git a/src/uu/tar/src/options/options.rs b/src/uu/tar/src/options/options.rs new file mode 100644 index 0000000..8f4ab59 --- /dev/null +++ b/src/uu/tar/src/options/options.rs @@ -0,0 +1,92 @@ +use crate::errors::TarError; +use crate::operations::OperationKind; +use clap::ArgMatches; +use std::path::PathBuf; +use uucore::error::UResult; + +/// [`TarParams`] Holds common information that is parsed from +/// command line arguments. That changes the current execution of +/// tar. +#[allow(dead_code)] +#[derive(Default)] +pub struct TarParams { + archive: PathBuf, + files: Vec, + options: Vec, +} + +impl From<&ArgMatches> for TarParams { + fn from(matches: &ArgMatches) -> TarParams { + let mut ops = Self::default(); + + // -v --verbose + if matches.get_flag("verbose") { + ops.options_mut().push(TarOption::Verbose); + } + + // [FILES]... + if let Some(files) = matches.get_many::("files") { + ops.files = files.map(|x| x.to_owned()).collect(); + } + + // -f --file + if let Some(a) = matches.get_one::("file") { + ops.archive = a.to_owned(); + } + + ops + } +} + +impl TarParams { + /// Convence method that parses the [`ArgMatches`] + /// processed by clap into [`TarParams`] and selects + /// the appropriate [`OperationKind`] for execution given back to the caller in a + /// tuple of ([`OperationKind`], [`TarParams`]) + pub fn with_operation(matches: &ArgMatches) -> UResult<(OperationKind, Self)> { + if matches.get_flag("create") { + Ok((OperationKind::Create, Self::from(matches))) + } else if matches.get_flag("extract") { + Ok((OperationKind::Extract, Self::from(matches))) + } else { + // TODO: update messaging + Err(Box::new(TarError::TarOperationError( + "Error processing: Operations".to_string(), + ))) + } + } +} + +#[allow(dead_code)] +impl TarParams { + pub fn files(&self) -> &Vec { + &self.files + } + pub fn files_mut(&mut self) -> &mut Vec { + &mut self.files + } + pub fn archive(&self) -> &PathBuf { + &self.archive + } + pub fn archive_mut(&mut self) -> &mut PathBuf { + &mut self.archive + } + pub fn options(&self) -> &Vec { + &self.options + } + pub fn options_mut(&mut self) -> &mut Vec { + &mut self.options + } +} + +/// [`TarOption`] Enum of avaliable tar options for later use +/// by [`TarOperation`] impls, eg. List, Create, Delete +#[allow(dead_code)] +pub enum TarOption { + AbsoluteNames, + ACLs, + AfterDate, + Anchored, + AtimePreserve { arg: String }, + Verbose, +} diff --git a/src/uu/tar/src/tar.rs b/src/uu/tar/src/tar.rs index 4db1bd5..b7d14bc 100644 --- a/src/uu/tar/src/tar.rs +++ b/src/uu/tar/src/tar.rs @@ -5,9 +5,12 @@ pub mod errors; mod operations; +mod options; use clap::{arg, crate_version, ArgAction, Command}; -use std::path::{Path, PathBuf}; +use operations::operation::TarOperation; +use options::TarParams; +use std::path::PathBuf; use uucore::error::UResult; use uucore::format_usage; @@ -33,43 +36,9 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uu_app().try_get_matches_from(args_to_parse)?; - let verbose = matches.get_flag("verbose"); + let (op, options) = TarParams::with_operation(&matches)?; - // Handle extract operation - if matches.get_flag("extract") { - let archive_path = matches.get_one::("file").ok_or_else(|| { - uucore::error::USimpleError::new(1, "option requires an argument -- 'f'") - })?; - - return operations::extract::extract_archive(archive_path, verbose); - } - - // Handle create operation - if matches.get_flag("create") { - let archive_path = matches.get_one::("file").ok_or_else(|| { - uucore::error::USimpleError::new(1, "option requires an argument -- 'f'") - })?; - - let files: Vec<&Path> = matches - .get_many::("files") - .map(|v| v.map(|p| p.as_path()).collect()) - .unwrap_or_default(); - - if files.is_empty() { - return Err(uucore::error::USimpleError::new( - 1, - "Cowardly refusing to create an empty archive", - )); - } - - return operations::create::create_archive(archive_path, &files, verbose); - } - - // If no operation specified, show error - Err(uucore::error::USimpleError::new( - 1, - "You must specify one of the '-c' or '-x' options", - )) + op.exec(&options) } #[allow(clippy::cognitive_complexity)]