EAF24N2DSDJOHAUHTLKC7P5N7N2PQQU7WGZ5UXPN6WU2MHX2E3CAC pub mod calc;pub mod cat;pub mod cd;pub mod echo;pub mod help;pub mod ls;
use crate::commands::is_piped;/// List dir entries. Take the args part of `ShellCommand`.pub fn ls(mut args: Vec<String>) -> String {let mut output = String::new();if args.is_empty() {args.push(".".to_string());}is_piped(&args, "ls");let mut path_idx = 0;for (idx, arg) in args.iter().enumerate() {if !arg.starts_with("--") || !arg.starts_with('-') {path_idx = idx;}}let input = &args[path_idx];let path;if std::path::Path::new(input).exists() {path = std::fs::read_dir(input).unwrap();} else {println!("ERROR: '{}' is not a valid file or directory.", input);return String::from("");}for file in path {let raw_entry = file.unwrap().path();#[cfg(any(target_os = "android", target_os = "linux"))]let still_raw_entry = raw_entry.to_str().unwrap().replace("./", "");#[cfg(target_os = "windows")]let still_raw_entry = raw_entry.to_str().unwrap().replace(".\\", "");let paths = still_raw_entry.split('\n');for line in paths {#[cfg(any(target_os = "android", target_os = "linux"))]let parts = line.split('/');#[cfg(target_os = "windows")]let parts = line.split('\\');let mut n = 0;#[cfg(any(target_os = "android", target_os = "linux"))]let parts_count = line.split('/').count();#[cfg(target_os = "windows")]let parts_count = line.split('\\').count();for part in parts {#[cfg(feature = "colors")]{#[cfg(feature = "colors")]use colored::Colorize;if part.starts_with('.') || n == parts_count - 1 {#[cfg(feature = "colors")]output.push_str(format!("{}", part.white()).as_str());} else {#[cfg(feature = "colors")]output.push_str(format!("{}", part.green()).as_str());}}#[cfg(not(feature = "colors"))]output.push_str(&part.to_string());n += 1;if n == parts_count {break;}output.push('/');}output.push('\n');}}output}
/// Prints a list of builtin commands.pub fn help(args: &[String]) {if args.is_empty() {println!("\crust [https://github.com/Phate6660/crust]\n\builtins:\n\---------\n\calc\n\cd\n\echo\n\exit\n\help\n\ls\n\pwd\n\prompt");return;}if args.len() > 1 {println!("Please specify only one command.");return;}match args[0].as_str() {"calc" => println!("Supports +, -, /, x for two numbers.\nE.g. 1+1, 4/2, 2x4, 2-1"),"cat" => println!("Support printing file output, and additionally showing line numbers with `-n`."),"cd" => {println!("\Takes a absolute or relative path and changes directory to it.\n\`cd -` will take you to your previous dir.");}"echo" => println!("Takes n amount of arguments and prints them to stdout."),"exit" => println!("Exits the shell with the given exit code."),"help" => println!("Returns information about the builtin commands."),"ls" => println!("Lists the content of a directory."),"pwd" => println!("Prints the working directory."),"prompt" => {println!("\Can be set to a static string, by just setting the string in the PROMPT env variable,\n\or can be set to a dynamic prompt, by including a command to be executed, by delimiting it with %(),\in the prompt string.\n\Variables are also supported, such as %{{U}} for user, %{{C}} for CWD, etc.\n\e.G.: `%(whoami)E@%(hostname)> `. This will, for my case, produce `zeno@aether> `.");}_ => {println!("\cRUSTy [https://github.com/Phate6660/crust]\n\builtins:\n\---------\n\calc\n\cd\n\echo\n\exit\n\help\n\ls\n\pwd\n\prompt");}}}
use crate::commands::is_piped;/// Just like you know it. Takes the args part of `ShellCommand` and prints them.pub fn echo(args: &[String]) -> String {let mut output = String::new();is_piped(args, "echo");for arg in args {// TODO: Support other escape sequences.let arg_to_push = arg.replace("\\n", "\n"); // Needed to replace \n with newline.output.push_str(format!("{} ", arg_to_push).as_str());}output}
use crate::commands::ShellCommand;use crate::shared_functions::ShellState;use std::env::current_dir;/// Helper for cd, to actually change the dirctory.fn cd_helper(dir: &str) {let path = std::path::Path::new(dir);match std::env::set_current_dir(&path) {Ok(()) => (),Err(_) => println!("Failed to change directory to '{}'", path.display()),}}/// Used to change directory./// Takes a `ShellState` and `ShellCommand`./// `ShellState` is used to realize `cd -` fuctionality,/// but can be used for other options in the future.pub fn cd(shell_state: &mut ShellState, command: &ShellCommand) {if command.args.is_empty() {shell_state.cd_prev_dir = Some(current_dir().unwrap());let user = std::env::var("USER").unwrap();let home = ["/home/", user.as_str()].concat();cd_helper(&home);} else if command.args[0] == "-" {if shell_state.cd_prev_dir.is_none() {println!("No previous dir found");return;}// unwrap can be safely used here, because function would've returned// if cd_prev_dir is Nonematch &shell_state.cd_prev_dir.as_ref().unwrap().to_str() {Some(path) => cd_helper(path),None => {println!("Could not convert Path to String (src/buildins.rs in function cd)");shell_state.cd_prev_dir = None;}}shell_state.cd_prev_dir = Some(current_dir().unwrap());} else {shell_state.cd_prev_dir = Some(current_dir().unwrap());cd_helper(&command.args[0]);}}
use crate::commands::is_piped;use sflib::{line, read};pub fn cat(args: &[String]) -> String {is_piped(args, "cat");match args[0].as_str() {"-l" => {let line_number = args[1].parse::<usize>().unwrap() - 1; // -1 to account for 0-indexing.line(&args[2], line_number).unwrap()}"-n" => {let mut final_output = String::new();let output = read(&args[1]).unwrap();let output_vec = output.split('\n');for (idx, line) in output_vec.enumerate() {let string = format!("{} {}\n", idx, line);final_output.push_str(&string);}final_output}_ => {let contents = read(&args[0]);if contents.is_err() {format!("[cat: '{}']: No such file or directory.", args[0])} else {contents.unwrap()}}}}
use crate::commands::is_piped;/// Get the calculator vars (`math_op`, `first_number`, `second_number`) for calc.pub fn get_calc_vars(problem: &str) -> (&str, i32, i32) {let math_op = if problem.contains('x') {"x"} else if problem.contains('*') {"*"} else if problem.contains('/') {"/"} else if problem.contains('%') {"%"} else if problem.contains('+') {"+"} else if problem.contains('-') {"-"} else {""};let problem_vector: Vec<&str> = problem.split(math_op).collect();let first_number: i32 = problem_vector[0].parse().unwrap();let second_number: i32 = problem_vector[1].parse().unwrap();(math_op, first_number, second_number)}/// Takes the `args` part of a `ShellCommand` struct,/// and tries to evaluate the given mathematical expression,/// returning a String with the result.pub fn calc(args: &[String]) -> String {let mut output = String::new();is_piped(args, "calc");let problem = args.concat();let (math_op, first_number, second_number) = get_calc_vars(&problem);match math_op {// Multiplication"x" | "*" => output.push_str(format!("{}", first_number * second_number).as_str()),// Division"/" => output.push_str(format!("{}", first_number / second_number).as_str()),// Division with remainder"%" => output.push_str(format!("{}", first_number % second_number).as_str()),// Addition"+" => output.push_str(format!("{}", first_number + second_number).as_str()),// Subtraction"-" => output.push_str(format!("{}", first_number - second_number).as_str()),_ => output.push_str(format!("Error, '{}' is an unsupported operation.", math_op).as_str()),}output}