diff --git a/src/builtins/fetch.rs b/src/builtins/fetch.rs index f07ea29..c334980 100644 --- a/src/builtins/fetch.rs +++ b/src/builtins/fetch.rs @@ -20,7 +20,7 @@ impl Builtin for Fetch { "@".bold(), hostname().bright_green().bold() ); - println!("{}", line); + println!("{line}"); println!( "{} {} {}", "OS".bright_blue().bold(), @@ -50,7 +50,7 @@ fn format_uptime(uptime: u64) -> String { let m = (uptime - h * 3600) / 60; let s = uptime - (h * 3600 + m * 60); - format!("{:.0}h {:.0}m {:.0}s", h, m, s) + format!("{h:.0}h {m:.0}m {s:.0}s") } fn bytes_to_mebi_bytes(bytes: u64) -> u64 { diff --git a/src/builtins/help.rs b/src/builtins/help.rs index 1e54065..5608cef 100644 --- a/src/builtins/help.rs +++ b/src/builtins/help.rs @@ -1,6 +1,6 @@ -use colored::Colorize; use crate::builtins::{Builtin, BuiltinConfig}; use crate::error::ShellError; +use colored::Colorize; pub struct Help; @@ -21,18 +21,22 @@ impl Builtin for Help { "; for line in commands.lines() { - println!("{}{}", split_command(line)[0].bold(), split_command(line)[1]); + println!( + "{}{}", + split_command(line)[0].bold(), + split_command(line)[1] + ); } Ok(()) } } fn split_command(commands: &str) -> Vec<&str> { - if commands.trim_start().len() >= 1 { - let splitted_command = commands.trim_start().splitn(2, " ").collect(); + if !commands.trim_start().is_empty() { + let splitted_command = commands.trim_start().splitn(2, ' ').collect(); return splitted_command; } - return vec!["", ""]; + vec!["", ""] } #[cfg(test)] @@ -42,13 +46,22 @@ mod tests { #[test] fn test_split_command_split() { let test_string1 = "Hallo, Marcel Davis 1&1."; - assert_eq!(split_command(test_string1), vec!["Hallo,", "Marcel Davis 1&1."]); + assert_eq!( + split_command(test_string1), + vec!["Hallo,", "Marcel Davis 1&1."] + ); let test_string2 = "Leiter1 23für Kundenzufriedenheit."; - assert_eq!(split_command(test_string2), vec!["Leiter1", "23für Kundenzufriedenheit."]); + assert_eq!( + split_command(test_string2), + vec!["Leiter1", "23für Kundenzufriedenheit."] + ); let test_string3 = "Wir# $%gehen erst wieder, wenn ihre Leitung läuft!"; - assert_eq!(split_command(test_string3), vec!["Wir#", "$%gehen erst wieder, wenn ihre Leitung läuft!"]); + assert_eq!( + split_command(test_string3), + vec!["Wir#", "$%gehen erst wieder, wenn ihre Leitung läuft!"] + ); } #[test] @@ -60,4 +73,4 @@ mod tests { fn test_split_command_only_space() { assert_eq!(split_command(" "), vec!["", ""]); } -} \ No newline at end of file +} diff --git a/src/builtins/ls.rs b/src/builtins/ls.rs index d621b81..f2a2a28 100644 --- a/src/builtins/ls.rs +++ b/src/builtins/ls.rs @@ -1,4 +1,8 @@ -use std::{fs::metadata, path::PathBuf, str::FromStr}; +use std::{ + fs::{metadata, Permissions}, + path::PathBuf, + str::FromStr, +}; use chrono::{DateTime, Datelike, Local}; @@ -19,7 +23,7 @@ impl Builtin for Ls { //for entry in entries.by_ref().into_iter() {} println!( - "{} | dir | size | modified |", + "{} | dir | size | modified | accessed | created | readonly |", right_padding(" filename", 20) ); @@ -42,6 +46,18 @@ impl Builtin for Ls { Err(_) => Local::now(), }; + let accessed: DateTime = match metadata.accessed() { + Ok(t) => DateTime::from(t), + Err(_) => Local::now(), + }; + + let created: DateTime = match metadata.created() { + Ok(t) => DateTime::from(t), + Err(_) => Local::now(), + }; + + let permissions = metadata.permissions(); + let mut file_type = "unknown"; if metadata.file_type().is_dir() { file_type = "dir" @@ -52,7 +68,16 @@ impl Builtin for Ls { } println!( "{}", - format_line(20, &file_name, file_type, metadata.len(), modified) + format_line( + 20, + &file_name, + file_type, + metadata.len(), + modified, + accessed, + created, + permissions + ) ); } Ok(()) @@ -86,13 +111,19 @@ fn format_line( file_type: &str, file_size: u64, modified: DateTime, + accessed: DateTime, + created: DateTime, + permissions: Permissions, ) -> String { format!( - "{} | {:4} | {:6} | {} |", + "{} | {:4} | {:6} | {:<10} | {:<10} | {:<10} | {:<5} |", right_padding(file_name, max_name_len), file_type, format_filesize(file_size), - right_padding(&format_date(modified), 10) + format_date(modified), + format_date(accessed), + format_date(created), + permissions.readonly().to_string(), ) } diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index bb764dc..4f6418b 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -14,6 +14,7 @@ mod pwd; mod quote; mod segfault; mod sus; +mod time; pub struct BuiltinConfig { pub prompt_style: PromptStyle, @@ -45,6 +46,8 @@ const BUILTINS: Lazy)>> = Lazy::new(|| { ("quote", Box::new(quote::Quote)), ("segfault", Box::new(segfault::Segfault)), ("sus", Box::new(sus::Sus)), + ("quote", Box::new(quote::Quote)), + ("time", Box::new(time::Time)), ] }); @@ -73,27 +76,27 @@ mod tests { #[test] fn test_is_builtin_cd() { - assert_eq!(is_builtin("cd"), true); + assert!(is_builtin("cd")); } #[test] fn test_is_builtin_change_prompt() { - assert_eq!(is_builtin("change-prompt"), true); + assert!(is_builtin("change-prompt")); } #[test] fn test_is_builtin_exit() { - assert_eq!(is_builtin("exit"), true); + assert!(is_builtin("exit")); } #[test] fn test_is_builtin_fetch() { - assert_eq!(is_builtin("fetch"), true); + assert!(is_builtin("fetch")); } #[test] fn test_is_builtin_help() { - assert_eq!(is_builtin("help"), true); + assert!(is_builtin("help")); } #[test] @@ -103,36 +106,41 @@ mod tests { #[test] fn test_is_builtin_ls() { - assert_eq!(is_builtin("ls"), true); + assert!(is_builtin("ls")); } #[test] fn test_is_builtin_open() { - assert_eq!(is_builtin("open"), true); + assert!(is_builtin("open")); } #[test] fn test_is_builtin_pwd() { - assert_eq!(is_builtin("pwd"), true); + assert!(is_builtin("pwd")); } #[test] fn test_is_builtin_segfault() { - assert_eq!(is_builtin("segfault"), true); + assert!(is_builtin("segfault")); } #[test] fn test_is_builtin_sus() { - assert_eq!(is_builtin("sus"), true); + assert!(is_builtin("sus")); } #[test] fn test_is_builtin_quote() { - assert_eq!(is_builtin("quote"), true); + assert!(is_builtin("quote")); + } + + #[test] + fn test_is_builtin_time() { + assert!(is_builtin("time")); } #[test] fn test_is_builtin_notabuiltin() { - assert_eq!(is_builtin("notabuiltin"), false) + assert!(!is_builtin("notabuiltin")) } } diff --git a/src/builtins/time.rs b/src/builtins/time.rs new file mode 100644 index 0000000..a831e99 --- /dev/null +++ b/src/builtins/time.rs @@ -0,0 +1,36 @@ +use chrono::{DateTime, Local}; + +use crate::error::ShellError; + +use super::{Builtin, BuiltinConfig}; + +pub struct Time; + +impl Builtin for Time { + fn execute(&mut self, _: &mut BuiltinConfig, args: Vec) -> Result<(), ShellError> { + let time = Local::now(); + + let formatting = args.get(0).cloned().unwrap_or_default(); + let time_string = format_time(time, &formatting); + println!("{time_string}"); + Ok(()) + } +} + +fn format_time(time: DateTime, formatting: &str) -> String { + match formatting { + "RFC2822" => time.to_rfc2822(), + "RFC3339" => time.to_rfc3339(), + _ => time.format("%T").to_string(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_time() { + assert_eq!(Time.execute(&mut BuiltinConfig::new(), vec![]), Ok(())) + } +} diff --git a/src/error.rs b/src/error.rs index b73f6ed..ef27eff 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,11 +12,11 @@ pub enum ShellError { impl PartialEq for ShellError { fn eq(&self, other: &Self) -> bool { match self { - &ShellError::EmptyLine => matches!(other, ShellError::EmptyLine), - &ShellError::NotFound(_) => matches!(other, ShellError::NotFound(_)), - &ShellError::ExecuteFailure(_) => matches!(other, ShellError::ExecuteFailure(_)), - &ShellError::MalformedArgs(_) => matches!(other, ShellError::MalformedArgs(_)), - &ShellError::Interrupt => matches!(other, ShellError::Interrupt), + ShellError::EmptyLine => matches!(other, ShellError::EmptyLine), + ShellError::NotFound(_) => matches!(other, ShellError::NotFound(_)), + ShellError::ExecuteFailure(_) => matches!(other, ShellError::ExecuteFailure(_)), + ShellError::MalformedArgs(_) => matches!(other, ShellError::MalformedArgs(_)), + ShellError::Interrupt => matches!(other, ShellError::Interrupt), } } } diff --git a/src/execute.rs b/src/execute.rs index 566e055..191b510 100644 --- a/src/execute.rs +++ b/src/execute.rs @@ -10,16 +10,16 @@ use std::{io, thread}; /// This function is not directly in main.rs because it might be called by other function too (eg. /// when piping commands). pub fn interpret(line: String, config: &mut BuiltinConfig, ctrlc_recv: Receiver<()>) { - match try_interpret(line, config, ctrlc_recv.clone()) { + match try_interpret(line, config, ctrlc_recv) { Ok(_) | Err(ShellError::Interrupt) | Err(ShellError::EmptyLine) => (), Err(ShellError::NotFound(cmd)) => { eprintln!("{}: command not found", cmd.get_program().to_string_lossy()) } Err(ShellError::ExecuteFailure(msg)) => { - eprintln!("{}", msg) + eprintln!("{msg}") } Err(ShellError::MalformedArgs(args)) => { - eprintln!("Malformed arguments: {}", args) + eprintln!("Malformed arguments: {args}") } } }