From 34d583d33df7909bf62bf2b822746b8db86c9701 Mon Sep 17 00:00:00 2001 From: Guilleag01 Date: Fri, 10 Nov 2023 12:04:29 +0100 Subject: [PATCH] Recursive --- src/element.rs | 34 +++-- src/main.rs | 20 +-- src/out/default.rs | 4 +- src/out/list.rs | 152 +++++++++++++++++----- src/utils.rs | 45 ++++--- test/dir/another_dir/deepest_secret.txt | 0 test/dir/more_dirs/definetly_not_porn.zip | 0 test/dir/more_dirs/noodles.jpg | 0 test/dir/something_inside.txt | 0 9 files changed, 184 insertions(+), 71 deletions(-) create mode 100644 test/dir/another_dir/deepest_secret.txt create mode 100644 test/dir/more_dirs/definetly_not_porn.zip create mode 100644 test/dir/more_dirs/noodles.jpg create mode 100644 test/dir/something_inside.txt diff --git a/src/element.rs b/src/element.rs index 8a3d084..d8cd0b5 100644 --- a/src/element.rs +++ b/src/element.rs @@ -6,9 +6,11 @@ use std::{ time::SystemTime, }; +// use clap::builder::styling::Metadata; + use crate::utils::get_icon_file_type; -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Eq, PartialEq)] pub enum TypeOfFile { File, Dir, @@ -22,7 +24,7 @@ pub struct Element { path: String, file_type: TypeOfFile, name: String, - perms: fs::Permissions, + perms: Option, size: u64, creation: SystemTime, } @@ -31,7 +33,19 @@ impl Element { pub fn new(path_str: &str) -> Element { let path_built = Path::new(path_str); // println!("{:?}", path_built); - let metadata = fs::metadata(path_str).unwrap(); + let metadata: fs::Metadata; // = fs::metadata(path_str).unwrap(); + if let Result::Ok(m) = fs::metadata(path_str) { + metadata = m; + } else { + return Self { + path: path_str.to_string(), + file_type: TypeOfFile::File, + name: path_str.split('/').last().unwrap().to_string(), + perms: None, + size: 0, + creation: SystemTime::UNIX_EPOCH, + }; + } let symlink_metadata = fs::symlink_metadata(path_str).unwrap(); let t: TypeOfFile; @@ -66,7 +80,7 @@ impl Element { path: path_str.to_string(), file_type: t, name, - perms: metadata.permissions(), + perms: Some(metadata.permissions()), size: metadata.len(), creation: metadata.created().unwrap(), } @@ -84,7 +98,7 @@ impl Element { self.name.clone() } - pub fn get_perms(&self) -> fs::Permissions { + pub fn get_perms(&self) -> Option { self.perms.clone() } @@ -106,15 +120,15 @@ impl Display for Element { TypeOfFile::File => file_icon, TypeOfFile::Dir => { is_dir = true; - "  " + " " } - TypeOfFile::Link => " 󱅷 ", - TypeOfFile::Block => " 󰋊 ", - TypeOfFile::Socket => " 󰛳 ", + TypeOfFile::Link => "󱅷 ", + TypeOfFile::Block => "󰋊 ", + TypeOfFile::Socket => "󰛳 ", }; write!( f, - "{}{}{} ", + "{}{}{} ", icon, self.name.as_str(), if is_dir { "/" } else { "" } diff --git a/src/main.rs b/src/main.rs index 982b3fd..581115a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ use clap::Parser; use lsplus::{ - element::Element, out::{default::default, list::list}, + utils::get_elements_from_path, }; -use std::fs; // Needs to be defined in main #[derive(Parser, Debug)] @@ -17,6 +16,9 @@ pub struct Args { #[arg(short, long, default_value_t = false)] list: bool, + #[arg(short, long, default_value_t = 0)] + recursive: usize, + /// Path of the directory to list #[arg(default_value_t = String::from("."))] path: String, @@ -25,17 +27,17 @@ pub struct Args { fn main() { let args = Args::parse(); - let paths = fs::read_dir(args.path).unwrap(); + let elements = get_elements_from_path(args.path, args.all); - let _max_width = 50; + // let paths = fs::read_dir(args.path).unwrap(); - let elements: Vec = paths - .map(|e| Element::new(e.unwrap().path().to_str().unwrap())) - .filter(|element| args.all || !element.get_name().starts_with('.')) - .collect(); + // let elements: Vec = paths + // .map(|e| Element::new(e.unwrap().path().to_str().unwrap())) + // .filter(|element| args.all || !element.get_name().starts_with('.')) + // .collect(); if args.list { - list(elements); + list(elements, args.recursive); } else { default(elements); } diff --git a/src/out/default.rs b/src/out/default.rs index f788efe..a39645f 100644 --- a/src/out/default.rs +++ b/src/out/default.rs @@ -20,7 +20,7 @@ pub fn default(mut elements: Vec) { let mut max_len = 0; for k in 0..i { if i * j + k < elements.len() { - let space = get_string_length(&elements[i * j + k].to_string()); + let space = get_string_length(&elements[i * j + k].to_string()) + 1; if space > max_len { max_len = space; } @@ -39,7 +39,7 @@ pub fn default(mut elements: Vec) { for j in 0..num_columns { if i * j + k < elements.len() { print!( - "{}", + "{} ", pad_string(elements[i * j + k].to_string(), column_widths[j], true) ); } diff --git a/src/out/list.rs b/src/out/list.rs index 808cf4d..c4eb449 100644 --- a/src/out/list.rs +++ b/src/out/list.rs @@ -1,19 +1,16 @@ -use crate::element::Element; -use crate::utils::{get_size_string, get_string_length, pad_string, system_time_to_string}; +use crate::element::{Element, TypeOfFile}; +use crate::utils::{ + get_elements_from_path, get_size_string, get_string_length, pad_string, system_time_to_string, +}; -pub fn list(mut elements: Vec) { +pub fn list(mut elements: Vec, recursive_limit: usize) { elements.sort_unstable_by_key(|a: &Element| a.get_name()); let width = term_size::dimensions().unwrap().0; // ╭──────────────╼ File name ╾──────────────┬─╼ Size ╾─┬──╼ Creation ╾──╮ // │ some_example_file │ 420.69 G │ 01-01-70 00:00 │ // ╰─────────────────────────────────────────┴──────────┴────────────────╯ - let mut name_max_len = 0; - for e in &elements { - let length = get_string_length(e.to_string().as_str()); - if length > name_max_len { - name_max_len = length; - } - } + + let name_max_len = get_max_width(&elements, recursive_limit, 0); let name_length = name_max_len .max(14 + (elements.len() as f32).log10() as usize) @@ -21,41 +18,69 @@ pub fn list(mut elements: Vec) { .min(width - 30); print_header(name_length); - print_elements(&elements, name_length); + print_elements(&elements, name_length, recursive_limit, 0, &Vec::new()); print_footer(&elements, name_length); } fn print_header(name_length: usize) { print!("╭"); - for _ in 0..((name_length - 13) as f32 / 2.0).floor() as usize { + for _ in 0..((name_length - 12) as f32 / 2.0).floor() as usize { print!("─"); } print!("╼ File name ╾"); - for _ in 0..((name_length - 13) as f32 / 2.0).ceil() as usize { + for _ in 0..((name_length - 12) as f32 / 2.0).ceil() as usize { print!("─"); } println!("┬─╼ Size ╾─┬──╼ Creation ╾──╮"); } -fn print_elements(elements: &Vec, name_length: usize) { - for e in elements { +fn print_elements( + elements: &Vec, + name_length: usize, + recursive_limit: usize, + current_depth: usize, + is_last_element: &[bool], +) { + let mut new_is_last_element = is_last_element.to_owned(); + + // println!("{:?}", is_last_element); + + for (i, e) in elements.iter().enumerate() { let str_len = get_string_length(e.get_name().as_str()); - print!("│"); + print!("│ "); if get_string_length(e.to_string().as_str()) > name_length { + let mut e_string = String::new(); + if current_depth > 0 { + for &is_last in &new_is_last_element[1..] { + if is_last { + e_string.push_str(" "); + } else { + e_string.push_str("│ "); + } + } + if i == elements.len() - 1 { + e_string.push_str("╰─"); + } else { + e_string.push_str("├─"); + } + } + e_string.push_str(e.to_string().as_str()); print!( "{}", pad_string( - e.to_string().as_str()[..=name_length].to_string(), - name_length, + e_string.as_str()[..=name_length].to_string(), + name_length - 2, true ) ); - print!("│"); - print!("{}", pad_string(get_size_string(e.get_size()), 10, false)); - print!("│"); - print!(" {} ", system_time_to_string(e.get_creation())); - println!("│"); - + print_size_and_creation_date(e); + let mut e_name = String::new(); + if current_depth > 0 { + for _ in 0..current_depth { + e_name.push_str(" "); + } + } + e_name.push_str(e.get_name().as_str()); for i in 1..(str_len / (name_length - 5) + 1) { print!( "│ {}", @@ -71,20 +96,59 @@ fn print_elements(elements: &Vec, name_length: usize) { println!("│ │ │"); } } else { - print!("{}", pad_string(e.to_string(), name_length, true)); - print!("│"); - print!("{}", pad_string(get_size_string(e.get_size()), 10, false)); - print!("│"); - print!(" {} ", system_time_to_string(e.get_creation())); - println!("│"); + let mut e_string = String::new(); + if current_depth > 0 { + for &is_last in &new_is_last_element[1..] { + if is_last { + e_string.push_str(" "); + } else { + e_string.push_str("│ "); + } + } + + if i == elements.len() - 1 { + e_string.push_str("╰─"); + } else { + e_string.push_str("├─"); + } + } + e_string.push_str(e.to_string().as_str()); + print!("{}", pad_string(e_string, name_length, true)); + print_size_and_creation_date(e) + } + + if e.get_file_type() == TypeOfFile::Dir && current_depth < recursive_limit { + let dir_path = e.get_path_string(); + new_is_last_element.push(i == elements.len() - 1); + print_elements( + &get_elements_from_path(dir_path, true), + name_length, + recursive_limit, + current_depth + 1, + &new_is_last_element, + ); + new_is_last_element.pop(); + // println!("{:?}", is_last_element); } } } +fn print_size_and_creation_date(e: &Element) { + print!("│"); + if e.get_file_type() == TypeOfFile::Dir { + print!(" "); + } else { + print!("{}", pad_string(get_size_string(e.get_size()), 10, false)); + } + print!("│"); + print!(" {} ", system_time_to_string(e.get_creation())); + println!("│"); +} + fn print_footer(elements: &Vec, name_length: usize) { let num_elements = elements.len(); let num_elements_len = (num_elements as f32).log10() as usize; - let name_length_fixed = name_length - (num_elements_len + 14); + let name_length_fixed = name_length - (num_elements_len + 13); print!("╰"); for _ in 0..((name_length_fixed) as f32 / 2.0).floor() as usize { print!("─"); @@ -93,8 +157,28 @@ fn print_footer(elements: &Vec, name_length: usize) { for _ in 0..((name_length_fixed) as f32 / 2.0).ceil() as usize { print!("─"); } - // for _ in 0..(name_length - (num_elements_len + 15)) { - // print!("─"); - // } println!("┴──────────┴────────────────╯"); } + +fn get_max_width(elements: &Vec, recursive_limit: usize, current_depth: usize) -> usize { + let mut name_max_len = 0; + for e in elements { + let mut length = get_string_length(e.to_string().as_str()) + current_depth * 2; + if e.get_file_type() == TypeOfFile::Dir && current_depth < recursive_limit { + let recursive_width = get_max_width( + &get_elements_from_path(e.get_path_string(), true), + recursive_limit, + current_depth + 1, + ); + + if recursive_width > length { + length = recursive_width; + } + } + + if length > name_max_len { + name_max_len = length; + } + } + name_max_len +} diff --git a/src/utils.rs b/src/utils.rs index c5b7af6..0884b83 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,10 +1,23 @@ +use std::fs; use std::time::SystemTime; use chrono::offset::Utc; use chrono::DateTime; +use crate::element::Element; + +#[inline] +pub fn get_elements_from_path(path: String, all: bool) -> Vec { + fs::read_dir(path) + .unwrap() + .map(|e| Element::new(e.unwrap().path().to_str().unwrap())) + .filter(|element| all || !element.get_name().starts_with('.')) + .collect() +} + pub fn pad_string(s: String, pad: usize, after: bool) -> String { let mut s2 = String::new(); + // println!("{}, {}", s.len(), pad); if after { s2.push_str(s.as_str()); for _ in 0..(pad - get_string_length(&s)) { @@ -47,21 +60,21 @@ pub fn system_time_to_string(system_time: SystemTime) -> String { pub fn get_icon_file_type<'a>(filename: String) -> &'a str { let extension = filename.split('.').collect::>()[1..].join("."); match extension.to_lowercase().as_str() { - "zip" | "rar" | "7zip" | "tar" | "tar.gz" | "tgz" => " 󰗄 ", - "rs" => "  ", - "jpg" | "jpeg" | "png" | "bmp" | "gif" | "webp" | "svg" => " 󰋩 ", - "flv" | "avi" | "mp4" | "webm" | "mov" => "  ", - "exe" | "ini" | "bat" => "  ", - "py" => "  ", - "c" => "  ", - "cpp" => "  ", - "json" => "  ", - "pdf" => " 󰈦 ", - "java" | "jar" => "  ", - "js" => "  ", - "html" => "  ", - "css" => "  ", - "csv" => "  ", - _ => " 󰈔 ", + "zip" | "rar" | "7zip" | "tar" | "tar.gz" | "tgz" => "󰗄 ", + "rs" => " ", + "jpg" | "jpeg" | "png" | "bmp" | "gif" | "webp" | "svg" => "󰋩 ", + "flv" | "avi" | "mp4" | "webm" | "mov" => " ", + "exe" | "ini" | "bat" => " ", + "py" => " ", + "c" => " ", + "cpp" => " ", + "json" => " ", + "pdf" => "󰈦 ", + "java" | "jar" => " ", + "js" => " ", + "html" => " ", + "css" => " ", + "csv" => " ", + _ => "󰈔 ", } } diff --git a/test/dir/another_dir/deepest_secret.txt b/test/dir/another_dir/deepest_secret.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/dir/more_dirs/definetly_not_porn.zip b/test/dir/more_dirs/definetly_not_porn.zip new file mode 100644 index 0000000..e69de29 diff --git a/test/dir/more_dirs/noodles.jpg b/test/dir/more_dirs/noodles.jpg new file mode 100644 index 0000000..e69de29 diff --git a/test/dir/something_inside.txt b/test/dir/something_inside.txt new file mode 100644 index 0000000..e69de29