From 792536d6830d8eab8076d352430d614da568b537 Mon Sep 17 00:00:00 2001 From: erius Date: Sat, 27 Apr 2024 01:17:50 +0300 Subject: [PATCH] Implemented long output and inode output for rbls --- Cargo.lock | 176 ++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/bin/rbls.rs | 85 +++++++++++++++++++---- 3 files changed, 249 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a62628a..6af50bd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.13" @@ -50,6 +65,44 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets", +] + [[package]] name = "clap" version = "4.5.4" @@ -109,6 +162,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "encode_unicode" version = "0.3.6" @@ -121,6 +180,38 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -133,6 +224,27 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + [[package]] name = "proc-macro2" version = "1.0.81" @@ -155,6 +267,7 @@ dependencies = [ name = "rbutils" version = "0.1.0" dependencies = [ + "chrono", "clap", "console", ] @@ -194,6 +307,69 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 04d2c76..d275fa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +chrono = "0.4.38" clap = { version = "4.5.4", features = ["derive"] } console = "0.15.8" diff --git a/src/bin/rbls.rs b/src/bin/rbls.rs index 24c1be9..76655ba 100644 --- a/src/bin/rbls.rs +++ b/src/bin/rbls.rs @@ -1,8 +1,9 @@ use clap::Parser; +use chrono::{Local, TimeZone}; use std::io::{self, ErrorKind::*}; use std::fs::{self, DirEntry, Metadata}; +use std::os::unix::fs::{FileTypeExt, MetadataExt}; -// TODO: implement more options (-l, -i, etc.) fn main() { let cli = Cli::parse(); let mut non_dirs = Vec::::new(); @@ -28,12 +29,17 @@ fn main() { }; dirs.push(DirInfo::new(file_name, file_infos)); } + + let (dirs_len, non_dirs_len) = (dirs.len(), non_dirs.len()); + if !non_dirs.is_empty() { let non_dirs_filtered = transform_file_infos(non_dirs, &cli); let non_dirs_output = format_output(non_dirs_filtered, &cli); println!("{}", non_dirs_output); } - let dirs_output = if dirs.len() > 1 { + + if dirs.is_empty() { return; } + let dirs_output = if dirs_len + non_dirs_len > 1 { dirs.into_iter() .map(|dir| { let files_filtered = transform_file_infos(dir.file_infos, &cli); @@ -46,6 +52,8 @@ fn main() { let files_filtered = transform_file_infos(dir.file_infos, &cli); format_output(files_filtered, &cli) }; + + if non_dirs_len > 0 { println!(); } println!("{}", dirs_output); } @@ -76,28 +84,79 @@ fn transform_file_infos(file_infos: Vec, options: &Cli) -> Vec filtered_file_infos.sort_by(|fi1, fi2| fi1.name.to_lowercase().cmp(&fi2.name.to_lowercase())); return filtered_file_infos.into_iter() .map(|file_info| { - let mut file_name = file_info.name; - if file_info.metadata.is_dir() { file_name.push('/') } - return file_name; + + return format_file_info(file_info, options); }) .collect(); } -fn format_output(file_names: Vec, options: &Cli) -> String { - let term = console::Term::stdout(); - if term.is_term() { return format_tabular(file_names, options, term.size().1); } - else { return file_names.join(" "); } +fn format_file_info(file_info: FileInfo, options: &Cli) -> String { + let mut entry = String::new(); + if options.inode { + entry.push_str(&file_info.metadata.ino().to_string()); + entry.push(' '); + } + if options.long { + entry.push_str(&long_format(&file_info)) + } else { + entry.push_str(&file_info.name) + } + if file_info.metadata.is_dir() { entry.push('/') } + return entry; } -fn format_tabular(mut file_names: Vec, options: &Cli, term_width: u16) -> String { - let max_file_name_len = file_names.iter().map(|n| n.len()).max().unwrap(); +fn long_format(file_info: &FileInfo) -> String { + format!("{} {} {} {} {} {} {}", permissions_str(&file_info.metadata), file_info.metadata.nlink(), + file_info.metadata.uid(), file_info.metadata.gid(), file_info.metadata.size(), + modified_str(&file_info.metadata), file_info.name) +} + +fn modified_str(metadata: &Metadata) -> String { + let date_time = Local.timestamp_opt(metadata.mtime(), 0).unwrap(); + return date_time.format("%b %d %H:%M").to_string(); +} + +fn permissions_str(metadata: &Metadata) -> String { + let ftype = metadata.file_type(); + let mut result = String::with_capacity(10); + result.push( + if ftype.is_dir() { 'd' } + else if ftype.is_symlink() { 'l' } + else if ftype.is_block_device() { 'b' } + else if ftype.is_char_device() { 'c' } + else if ftype.is_fifo() { 'p' } + else if ftype.is_socket() { 's' } + else { '-' } + ); + + let permission_chars: Vec = vec!['r', 'w', 'x']; + let permissions = metadata.mode(); + for i in 0..9 { + let bit = permissions >> (8 - i) & 1; + result.push( + if bit > 0 { permission_chars[i % 3] } + else { '-' } + ); + } + return result; +} + +fn format_output(file_names: Vec, options: &Cli) -> String { + let term = console::Term::stdout(); + if term.is_term() && !options.long { return format_tabular(file_names, term.size().1); } + return file_names.join("\n"); +} + +fn format_tabular(mut entries: Vec, term_width: u16) -> String { + if entries.is_empty() { return String::new(); } + let max_file_name_len = entries.iter().map(|n| n.len()).max().unwrap(); let elem_width = max_file_name_len + 2; let row_elems_amount = (term_width as usize / elem_width).max(1); - for (i, name) in file_names.iter_mut().enumerate() { + for (i, name) in entries.iter_mut().enumerate() { name.push_str(&" ".repeat(max_file_name_len - name.len() + 2)); if (i + 1) % row_elems_amount == 0 { name.push('\n'); } } - return file_names.join("").trim_end().to_string(); + return entries.join("").trim_end().to_string(); } struct DirInfo {