/ src / main.rs
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  }