Recursive

This commit is contained in:
Guilleag01
2023-11-10 12:04:29 +01:00
parent bdf1b21bb0
commit 34d583d33d
9 changed files with 184 additions and 71 deletions

View File

@@ -6,9 +6,11 @@ use std::{
time::SystemTime, time::SystemTime,
}; };
// use clap::builder::styling::Metadata;
use crate::utils::get_icon_file_type; use crate::utils::get_icon_file_type;
#[derive(Copy, Clone)] #[derive(Copy, Clone, Eq, PartialEq)]
pub enum TypeOfFile { pub enum TypeOfFile {
File, File,
Dir, Dir,
@@ -22,7 +24,7 @@ pub struct Element {
path: String, path: String,
file_type: TypeOfFile, file_type: TypeOfFile,
name: String, name: String,
perms: fs::Permissions, perms: Option<fs::Permissions>,
size: u64, size: u64,
creation: SystemTime, creation: SystemTime,
} }
@@ -31,7 +33,19 @@ impl Element {
pub fn new(path_str: &str) -> Element { pub fn new(path_str: &str) -> Element {
let path_built = Path::new(path_str); let path_built = Path::new(path_str);
// println!("{:?}", path_built); // 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 symlink_metadata = fs::symlink_metadata(path_str).unwrap();
let t: TypeOfFile; let t: TypeOfFile;
@@ -66,7 +80,7 @@ impl Element {
path: path_str.to_string(), path: path_str.to_string(),
file_type: t, file_type: t,
name, name,
perms: metadata.permissions(), perms: Some(metadata.permissions()),
size: metadata.len(), size: metadata.len(),
creation: metadata.created().unwrap(), creation: metadata.created().unwrap(),
} }
@@ -84,7 +98,7 @@ impl Element {
self.name.clone() self.name.clone()
} }
pub fn get_perms(&self) -> fs::Permissions { pub fn get_perms(&self) -> Option<fs::Permissions> {
self.perms.clone() self.perms.clone()
} }
@@ -106,15 +120,15 @@ impl Display for Element {
TypeOfFile::File => file_icon, TypeOfFile::File => file_icon,
TypeOfFile::Dir => { TypeOfFile::Dir => {
is_dir = true; is_dir = true;
" " ""
} }
TypeOfFile::Link => " 󱅷 ", TypeOfFile::Link => "󱅷 ",
TypeOfFile::Block => " 󰋊 ", TypeOfFile::Block => "󰋊 ",
TypeOfFile::Socket => " 󰛳 ", TypeOfFile::Socket => "󰛳 ",
}; };
write!( write!(
f, f,
"{}{}{} ", "{}{}{} ",
icon, icon,
self.name.as_str(), self.name.as_str(),
if is_dir { "/" } else { "" } if is_dir { "/" } else { "" }

View File

@@ -1,9 +1,8 @@
use clap::Parser; use clap::Parser;
use lsplus::{ use lsplus::{
element::Element,
out::{default::default, list::list}, out::{default::default, list::list},
utils::get_elements_from_path,
}; };
use std::fs;
// Needs to be defined in main // Needs to be defined in main
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
@@ -17,6 +16,9 @@ pub struct Args {
#[arg(short, long, default_value_t = false)] #[arg(short, long, default_value_t = false)]
list: bool, list: bool,
#[arg(short, long, default_value_t = 0)]
recursive: usize,
/// Path of the directory to list /// Path of the directory to list
#[arg(default_value_t = String::from("."))] #[arg(default_value_t = String::from("."))]
path: String, path: String,
@@ -25,17 +27,17 @@ pub struct Args {
fn main() { fn main() {
let args = Args::parse(); 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<Element> = paths // let elements: Vec<Element> = paths
.map(|e| Element::new(e.unwrap().path().to_str().unwrap())) // .map(|e| Element::new(e.unwrap().path().to_str().unwrap()))
.filter(|element| args.all || !element.get_name().starts_with('.')) // .filter(|element| args.all || !element.get_name().starts_with('.'))
.collect(); // .collect();
if args.list { if args.list {
list(elements); list(elements, args.recursive);
} else { } else {
default(elements); default(elements);
} }

View File

@@ -20,7 +20,7 @@ pub fn default(mut elements: Vec<Element>) {
let mut max_len = 0; let mut max_len = 0;
for k in 0..i { for k in 0..i {
if i * j + k < elements.len() { 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 { if space > max_len {
max_len = space; max_len = space;
} }
@@ -39,7 +39,7 @@ pub fn default(mut elements: Vec<Element>) {
for j in 0..num_columns { for j in 0..num_columns {
if i * j + k < elements.len() { if i * j + k < elements.len() {
print!( print!(
"{}", "{} ",
pad_string(elements[i * j + k].to_string(), column_widths[j], true) pad_string(elements[i * j + k].to_string(), column_widths[j], true)
); );
} }

View File

@@ -1,19 +1,16 @@
use crate::element::Element; use crate::element::{Element, TypeOfFile};
use crate::utils::{get_size_string, get_string_length, pad_string, system_time_to_string}; 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<Element>) { pub fn list(mut elements: Vec<Element>, recursive_limit: usize) {
elements.sort_unstable_by_key(|a: &Element| a.get_name()); elements.sort_unstable_by_key(|a: &Element| a.get_name());
let width = term_size::dimensions().unwrap().0; let width = term_size::dimensions().unwrap().0;
// ╭──────────────╼ File name ╾──────────────┬─╼ Size ╾─┬──╼ Creation ╾──╮ // ╭──────────────╼ File name ╾──────────────┬─╼ Size ╾─┬──╼ Creation ╾──╮
// │ some_example_file │ 420.69 G │ 01-01-70 00:00 │ // │ some_example_file │ 420.69 G │ 01-01-70 00:00 │
// ╰─────────────────────────────────────────┴──────────┴────────────────╯ // ╰─────────────────────────────────────────┴──────────┴────────────────╯
let mut name_max_len = 0;
for e in &elements { let name_max_len = get_max_width(&elements, recursive_limit, 0);
let length = get_string_length(e.to_string().as_str());
if length > name_max_len {
name_max_len = length;
}
}
let name_length = name_max_len let name_length = name_max_len
.max(14 + (elements.len() as f32).log10() as usize) .max(14 + (elements.len() as f32).log10() as usize)
@@ -21,41 +18,69 @@ pub fn list(mut elements: Vec<Element>) {
.min(width - 30); .min(width - 30);
print_header(name_length); print_header(name_length);
print_elements(&elements, name_length); print_elements(&elements, name_length, recursive_limit, 0, &Vec::new());
print_footer(&elements, name_length); print_footer(&elements, name_length);
} }
fn print_header(name_length: usize) { fn print_header(name_length: usize) {
print!(""); 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!("");
} }
print!("╼ File name ╾"); 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!(""); print!("");
} }
println!("┬─╼ Size ╾─┬──╼ Creation ╾──╮"); println!("┬─╼ Size ╾─┬──╼ Creation ╾──╮");
} }
fn print_elements(elements: &Vec<Element>, name_length: usize) { fn print_elements(
for e in elements { elements: &Vec<Element>,
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()); let str_len = get_string_length(e.get_name().as_str());
print!(""); print!(" ");
if get_string_length(e.to_string().as_str()) > name_length { 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!( print!(
"{}", "{}",
pad_string( pad_string(
e.to_string().as_str()[..=name_length].to_string(), e_string.as_str()[..=name_length].to_string(),
name_length, name_length - 2,
true true
) )
); );
print!(""); print_size_and_creation_date(e);
print!("{}", pad_string(get_size_string(e.get_size()), 10, false)); let mut e_name = String::new();
print!(""); if current_depth > 0 {
print!(" {} ", system_time_to_string(e.get_creation())); for _ in 0..current_depth {
println!(""); e_name.push_str(" ");
}
}
e_name.push_str(e.get_name().as_str());
for i in 1..(str_len / (name_length - 5) + 1) { for i in 1..(str_len / (name_length - 5) + 1) {
print!( print!(
"{}", "{}",
@@ -71,20 +96,59 @@ fn print_elements(elements: &Vec<Element>, name_length: usize) {
println!("│ │ │"); println!("│ │ │");
} }
} else { } else {
print!("{}", pad_string(e.to_string(), name_length, true)); let mut e_string = String::new();
print!(""); if current_depth > 0 {
print!("{}", pad_string(get_size_string(e.get_size()), 10, false)); for &is_last in &new_is_last_element[1..] {
print!(""); if is_last {
print!(" {} ", system_time_to_string(e.get_creation())); e_string.push_str(" ");
println!(""); } 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<Element>, name_length: usize) { fn print_footer(elements: &Vec<Element>, name_length: usize) {
let num_elements = elements.len(); let num_elements = elements.len();
let num_elements_len = (num_elements as f32).log10() as usize; 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!(""); print!("");
for _ in 0..((name_length_fixed) as f32 / 2.0).floor() as usize { for _ in 0..((name_length_fixed) as f32 / 2.0).floor() as usize {
print!(""); print!("");
@@ -93,8 +157,28 @@ fn print_footer(elements: &Vec<Element>, name_length: usize) {
for _ in 0..((name_length_fixed) as f32 / 2.0).ceil() as usize { for _ in 0..((name_length_fixed) as f32 / 2.0).ceil() as usize {
print!(""); print!("");
} }
// for _ in 0..(name_length - (num_elements_len + 15)) {
// print!("─");
// }
println!("┴──────────┴────────────────╯"); println!("┴──────────┴────────────────╯");
} }
fn get_max_width(elements: &Vec<Element>, 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
}

View File

@@ -1,10 +1,23 @@
use std::fs;
use std::time::SystemTime; use std::time::SystemTime;
use chrono::offset::Utc; use chrono::offset::Utc;
use chrono::DateTime; use chrono::DateTime;
use crate::element::Element;
#[inline]
pub fn get_elements_from_path(path: String, all: bool) -> Vec<Element> {
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 { pub fn pad_string(s: String, pad: usize, after: bool) -> String {
let mut s2 = String::new(); let mut s2 = String::new();
// println!("{}, {}", s.len(), pad);
if after { if after {
s2.push_str(s.as_str()); s2.push_str(s.as_str());
for _ in 0..(pad - get_string_length(&s)) { 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 { pub fn get_icon_file_type<'a>(filename: String) -> &'a str {
let extension = filename.split('.').collect::<Vec<&str>>()[1..].join("."); let extension = filename.split('.').collect::<Vec<&str>>()[1..].join(".");
match extension.to_lowercase().as_str() { match extension.to_lowercase().as_str() {
"zip" | "rar" | "7zip" | "tar" | "tar.gz" | "tgz" => " 󰗄 ", "zip" | "rar" | "7zip" | "tar" | "tar.gz" | "tgz" => "󰗄 ",
"rs" => " ", "rs" => "",
"jpg" | "jpeg" | "png" | "bmp" | "gif" | "webp" | "svg" => " 󰋩 ", "jpg" | "jpeg" | "png" | "bmp" | "gif" | "webp" | "svg" => "󰋩 ",
"flv" | "avi" | "mp4" | "webm" | "mov" => " ", "flv" | "avi" | "mp4" | "webm" | "mov" => "",
"exe" | "ini" | "bat" => " ", "exe" | "ini" | "bat" => "",
"py" => " ", "py" => "",
"c" => " ", "c" => "",
"cpp" => " ", "cpp" => "",
"json" => " ", "json" => "",
"pdf" => " 󰈦 ", "pdf" => "󰈦 ",
"java" | "jar" => " ", "java" | "jar" => "",
"js" => " ", "js" => "",
"html" => " ", "html" => "",
"css" => " ", "css" => "",
"csv" => " ", "csv" => "",
_ => " 󰈔 ", _ => "󰈔 ",
} }
} }

View File

View File

View File