aboutsummaryrefslogtreecommitdiff
path: root/crates/whirl_prompt/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/whirl_prompt/src/lib.rs')
-rw-r--r--crates/whirl_prompt/src/lib.rs150
1 files changed, 150 insertions, 0 deletions
diff --git a/crates/whirl_prompt/src/lib.rs b/crates/whirl_prompt/src/lib.rs
new file mode 100644
index 0000000..32b247f
--- /dev/null
+++ b/crates/whirl_prompt/src/lib.rs
@@ -0,0 +1,150 @@
+// Copyleft (ɔ) 2021-2021 The Whirlsplash Collective
+// SPDX-License-Identifier: GPL-3.0-only
+
+#![feature(
+ type_ascription,
+ hash_set_entry,
+ type_name_of_val,
+ decl_macro,
+ proc_macro_hygiene
+)]
+#![warn(rust_2018_idioms)]
+#![recursion_limit = "128"]
+
+mod builtins;
+mod constants;
+mod structure;
+
+use std::{io, io::Write, str::FromStr};
+
+use whirl_config::Config;
+
+use crate::{
+ builtins::{
+ builtin_cat,
+ builtin_config,
+ builtin_echo,
+ builtin_fetch,
+ builtin_help,
+ builtin_history,
+ builtin_ls,
+ structures::BuiltIn,
+ },
+ structure::Command,
+};
+
+pub struct Prompt {
+ history: Vec<String>,
+}
+impl Prompt {
+ pub async fn handle() -> ! {
+ let mut prompt = Prompt {
+ history: vec![]
+ };
+
+ loop {
+ Prompt::write_prompt();
+ let command = prompt.read_command();
+ prompt
+ .process_command(Prompt::tokenize_command(command))
+ .await;
+ }
+ }
+
+ fn write_prompt() {
+ print!("{} ", Config::get().whirlsplash.prompt.ps1);
+ io::stdout().flush().unwrap();
+ }
+
+ fn read_command(&mut self) -> String {
+ let mut input = String::new();
+ io::stdin()
+ .read_line(&mut input)
+ .expect("failed to read command from stdin");
+
+ if input.len() <= 2 {
+ input = "null".to_string();
+ }
+
+ input
+ }
+
+ fn tokenize_command(c: String) -> Command {
+ let mut command_split: Vec<String> = c.split_whitespace().map(|s| s.to_string()).collect();
+
+ Command {
+ keyword: command_split.remove(0),
+ args: command_split,
+ }
+ }
+
+ // TODO: Find a way to make this access itself `history` doesn't have to be
+ // passed everytime.
+ async fn process_command(&mut self, c: Command) -> i32 {
+ let exit_code = match BuiltIn::from_str(&c.keyword) {
+ Ok(BuiltIn::Echo) => builtin_echo(&c.args),
+ Ok(BuiltIn::Exit) => std::process::exit(0),
+ Ok(BuiltIn::History) => builtin_history(&self.history),
+ Ok(BuiltIn::Null) => 0,
+ Ok(BuiltIn::Help) => builtin_help(),
+ Ok(BuiltIn::Ls) => builtin_ls(),
+ Ok(BuiltIn::Cat) => builtin_cat(&c.args).await,
+ Ok(BuiltIn::Config) => builtin_config(&c.args),
+ Ok(BuiltIn::Fetch) => builtin_fetch(),
+ _ => {
+ println!("wsh: command not found: {}", &c.keyword);
+ 1
+ }
+ };
+
+ if c.keyword != "null" {
+ self.history.push(c.to_line());
+ }
+
+ exit_code
+ }
+}
+
+#[cfg(test)]
+mod tokenize_command {
+ use crate::Prompt;
+
+ #[test]
+ #[ignore]
+ fn empty_command() { assert_eq!("", Prompt::tokenize_command("".to_string()).keyword) }
+
+ #[test]
+ fn test_keyword() { assert_eq!("test", Prompt::tokenize_command("test".to_string()).keyword) }
+
+ #[test]
+ fn no_arg() { assert_eq!(0, Prompt::tokenize_command("test".to_string()).args.len()) }
+
+ #[test]
+ fn one_arg() {
+ assert_eq!(
+ 1,
+ Prompt::tokenize_command("test one".to_string()).args.len()
+ )
+ }
+
+ #[test]
+ fn multi_arg() {
+ assert_eq!(
+ 3,
+ Prompt::tokenize_command("test one two three".to_string())
+ .args
+ .len()
+ )
+ }
+
+ #[test]
+ #[ignore]
+ fn quotes() {
+ assert_eq!(
+ 2,
+ Prompt::tokenize_command("test \"one two\" three".to_string())
+ .args
+ .len()
+ )
+ }
+}