main.rs
1 use std::io::{self, Write}; 2 3 fn iterate_path<'a>(pwd: &'a str, mut goto: Vec<&'a str>, home: &str) -> String { 4 let mut split_path = pwd.split("/").collect::<Vec<&str>>(); 5 let next = goto.remove(0); 6 if next == ".." { // Parent directory 7 split_path.pop(); 8 } else if next == "~" { // Home directory 9 split_path = home.split("/").collect::<Vec<&str>>() 10 } else { 11 if next != "." { // Folder in current directory, we ignore `.` because it does nothing 12 split_path.push(next); 13 } 14 } 15 // Rejoin into proper path 16 let new = &split_path.join("/"); 17 if goto.len() == 0 { // Nowhere left to move to, return 18 String::from(new.trim_end_matches("/")) 19 } else { // Keep navigating 20 iterate_path(new, goto, home) 21 } 22 } 23 24 fn main() { 25 let stdin = io::stdin(); 26 27 // Get env variables 28 let env = std::env::var("PATH").unwrap(); 29 let home = std::env::var("HOME").unwrap(); 30 31 // Set PWD based on where the command is run from 32 let mut pwd = String::from(std::env::current_dir().unwrap().to_str().unwrap()); 33 34 loop { 35 // Print prompt 36 print!("$ "); 37 io::stdout().flush().unwrap(); 38 39 // Wait for input and read into buffer `input` 40 let mut input = String::new(); 41 stdin.read_line(&mut input).unwrap(); 42 43 // List of builtin commands 44 let builtins = ["cd", "echo", "exit", "pwd", "type"]; 45 46 // Split command into args 47 let mut argv = input.trim().split(" ").collect::<Vec<&str>>(); 48 49 // Get command 50 let command = argv[0]; 51 52 // Set args to everything else 53 let args = argv.split_off(1); 54 55 // Determine which command is being run 56 match command { 57 "cd" => { 58 if args[0].len() == 0 { 59 return println!("Expecting directory") 60 }; 61 let goto = args[0]; 62 if goto.starts_with("/") { // Absolute paths 63 let path = std::fs::metadata(args[0]); 64 if path.is_ok() { 65 pwd = String::from(args[0]); 66 } else { 67 println!("cd: {}: No such file or directory", args[0]) 68 } 69 } else { 70 let new_path = iterate_path(&pwd, goto.split("/").collect::<Vec<&str>>(), &home); 71 let path = std::fs::metadata(&new_path); 72 if path.is_ok() { 73 pwd = new_path; 74 } else { 75 println!("cd: {}: No such file or directory", new_path) 76 } 77 } 78 }, 79 "echo" => { 80 // Join args into one string 81 let text = args.join(" "); 82 println!("{text}") 83 }, 84 "exit" => { 85 // Parse the arg into an int 86 let code: i32 = args[0].parse().unwrap(); 87 std::process::exit(code) 88 }, 89 "pwd" => { 90 println!("{pwd}") 91 }, 92 "type" => { 93 if args[0].len() == 0 { 94 return println!("Expecting command") 95 }; 96 // What command are we getting the type of 97 let subcommand = args[0]; 98 if builtins.contains(&subcommand) { // builtins 99 println!("{subcommand} is a shell builtin") 100 } else { 101 let mut split = env.split(":"); 102 103 if let Some(path) = 104 split.find(|path| std::fs::metadata(format!("{}/{}", path, subcommand)).is_ok()) 105 { // in path 106 println!("{subcommand} is {path}/{subcommand}") 107 } else { // whomp whomp 108 println!("{subcommand}: not found") 109 } 110 } 111 }, 112 _ => { 113 let mut split = env.split(":"); 114 115 if let Some(path) = 116 split.find(|path| std::fs::metadata(format!("{}/{}", path, command)).is_ok()) 117 { // Command in path, run with args 118 let full_command = format!("{}/{}", path, command); 119 120 let output = std::process::Command::new(full_command) 121 .args(args) 122 .output().unwrap(); 123 124 print!("{}", String::from_utf8(output.stdout).unwrap()) 125 } else { // Unknown command 126 println!("{command}: command not found") 127 } 128 } 129 }; 130 } 131 }