diff --git a/homework_2/ls/.idea/.gitignore b/homework_2/ls/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/homework_2/ls/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/homework_2/ls/.idea/ls.iml b/homework_2/ls/.idea/ls.iml new file mode 100644 index 0000000..c254557 --- /dev/null +++ b/homework_2/ls/.idea/ls.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/homework_2/ls/.idea/modules.xml b/homework_2/ls/.idea/modules.xml new file mode 100644 index 0000000..ce4a5a0 --- /dev/null +++ b/homework_2/ls/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/homework_2/ls/.idea/vcs.xml b/homework_2/ls/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/homework_2/ls/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/homework_2/ls/Cargo.lock b/homework_2/ls/Cargo.lock new file mode 100644 index 0000000..4d3603b --- /dev/null +++ b/homework_2/ls/Cargo.lock @@ -0,0 +1,223 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" + +[[package]] +name = "ls" +version = "0.1.0" +dependencies = [ + "structopt", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/homework_2/ls/Cargo.toml b/homework_2/ls/Cargo.toml new file mode 100644 index 0000000..627a9bb --- /dev/null +++ b/homework_2/ls/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "ls" +version = "0.1.0" +authors = ["Maksym-Yurii Rudko "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +structopt = "0.3.21" diff --git a/homework_2/ls/src/main.rs b/homework_2/ls/src/main.rs new file mode 100644 index 0000000..20f212a --- /dev/null +++ b/homework_2/ls/src/main.rs @@ -0,0 +1,74 @@ +mod models; +mod providers; + +use std::io; +use std::path::PathBuf; +use providers::file_provider; +use structopt::StructOpt; +use crate::models::FileModel; + +#[derive(StructOpt)] +struct CliArgs { + #[structopt(short, long = "human")] + human_readable: bool, + #[structopt(name = "Show all", short = "a", long = "all")] + show_hidden: bool, + #[structopt(parse(from_os_str), default_value = ".")] + start_dir: PathBuf, +} + +fn main() -> io::Result<()> { + let args: CliArgs = CliArgs::from_args(); + let start_path = args.start_dir.as_path(); + + // If the path provided to us is a directory, read its entries + if start_path.is_dir() { + match file_provider::get_files_in_directory(start_path) { + Ok(mut file_models) => { + let start_path = start_path.to_string_lossy(); + + sort_file_table(&mut file_models); + filter_file_table(&mut file_models, args.show_hidden); + print_file_table(start_path.as_ref(), &file_models, args.human_readable); + } + Err(e) => { + eprintln!("Error reading files in directory: {}", e); + return Err(e); + } + } + } else { + let parsed_file = file_provider::parse_dir_entry(start_path)?; + // If the path provided is a single file, just print its data + print_file_model(&parsed_file, args.human_readable); + } + + Ok(()) +} + +fn filter_file_table(file_table: &mut Vec, should_show_hidden: bool) { + file_table.retain(|f| !f.is_hidden || should_show_hidden) +} + +fn print_file_table(start_path: &str, file_table: &Vec, human_readable: bool) { + println!("List of files in {}", start_path); + println!("{:36} {:9}", "Name", "Size"); + + for model in file_table { + print_file_model(model, human_readable) + } +} + +fn print_file_model(model: &FileModel, human_readable: bool) { + println!( + "{:36} {:9}", + format!("{}{}", model.name, if model.is_directory { "/" } else { "" }), + if human_readable { model.size.to_human_str() } else { model.size.to_raw_str() } + ) +} + +fn sort_file_table(file_table: &mut Vec) { + file_table.sort_unstable_by(|a, b| a.is_directory + .cmp(&b.is_directory) + .reverse() + .then(a.name.cmp(&b.name))); +} diff --git a/homework_2/ls/src/models.rs b/homework_2/ls/src/models.rs new file mode 100644 index 0000000..dbcc0f8 --- /dev/null +++ b/homework_2/ls/src/models.rs @@ -0,0 +1,49 @@ +pub struct FileModel { + pub name: String, + pub is_hidden: bool, + pub is_directory: bool, + pub size: FileSize, + pub is_ro: bool, +} + +pub struct FileSize { + pub size: u64 +} + +impl FileSize { + pub fn to_raw_str(&self) -> String { + format!("{} B", self.size) + } + + pub fn to_human_str(&self) -> String { + if self.size < 1024 { + self.to_raw_str() + } else { + let mut i: u8 = 0; + let mut human_size: f64 = self.size as f64; + loop { + human_size /= 1024.0; + i += 1; + + if human_size < 1024.0 { + break; + } + } + format!("{:.2} {}", human_size, FileSize::get_size_label(i)) + } + } + + fn get_size_label(size_order: u8) -> String { + String::from( + match size_order { + 0 => "B", + 1 => "KiB", + 2 => "MiB", + 3 => "GiB", + 4 => "TiB", + 5 => "EiB", + _ => "B" + } + ) + } +} diff --git a/homework_2/ls/src/providers.rs b/homework_2/ls/src/providers.rs new file mode 100644 index 0000000..78b43fc --- /dev/null +++ b/homework_2/ls/src/providers.rs @@ -0,0 +1,33 @@ +pub mod file_provider { + use std::path::Path; + use crate::models::{FileModel, FileSize}; + use std::{io, fs}; + use std::io::Error; + + pub fn get_files_in_directory(directory: &Path) -> Result, Error> { + let mut file_vector = Vec::new(); + // Iterate over entries in the directory + for entry in fs::read_dir(directory)? { + let entry = entry?; + + file_vector.push(parse_dir_entry(&entry.path().as_path())?); + } + Ok(file_vector) + } + + pub fn parse_dir_entry(entry: &Path) -> io::Result { + let name = { + let os_file_name = entry.file_name() + .expect("Could not read file name from a given path"); + String::from(os_file_name.to_string_lossy()) + }; + let metadata = entry.metadata()?; + + let is_hidden = name.starts_with("."); + let is_directory = metadata.is_dir(); + let size: u64 = metadata.len(); + let is_ro = metadata.permissions().readonly(); + + Ok(FileModel { name, is_hidden, is_directory, size: FileSize { size }, is_ro }) + } +} \ No newline at end of file