diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cli.rs | 2 | ||||
| -rw-r--r-- | src/lib.rs | 1 | ||||
| -rw-r--r-- | src/prompt/builtins.rs | 27 | ||||
| -rw-r--r-- | src/prompt/mod.rs | 98 | ||||
| -rw-r--r-- | src/prompt/structure.rs | 7 | ||||
| -rw-r--r-- | src/subs.rs | 16 |
6 files changed, 142 insertions, 9 deletions
@@ -18,7 +18,7 @@ impl Cli { pub async fn execute(matches: ArgMatches<'_>) { if matches.is_present("run") { - run().await.unwrap(); + run().await; } else if let Some(cmd) = matches.subcommand_matches("config") { if cmd.is_present("show") { println!("{:#?}", Config::get().unwrap()); @@ -22,6 +22,7 @@ extern crate serde_derive; pub mod cli; pub mod config; +pub mod prompt; pub mod subs; pub mod api; diff --git a/src/prompt/builtins.rs b/src/prompt/builtins.rs new file mode 100644 index 0000000..0ede1d3 --- /dev/null +++ b/src/prompt/builtins.rs @@ -0,0 +1,27 @@ +// Copyleft (ɔ) 2021-2021 The Whirlsplash Collective +// SPDX-License-Identifier: GPL-3.0-only + +use std::str::FromStr; + +pub enum BuiltIn { + Echo, + History, + Exit, +} +impl FromStr for BuiltIn { + type Err = (); + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "echo" => Ok(BuiltIn::Echo), + "history" => Ok(BuiltIn::History), + "exit" => Ok(BuiltIn::Exit), + _ => Err(()), + } + } +} + +pub fn builtin_echo(args: &[String]) -> i32 { + println!("{}", args.join(" ")); + 0 +} diff --git a/src/prompt/mod.rs b/src/prompt/mod.rs new file mode 100644 index 0000000..a1329bf --- /dev/null +++ b/src/prompt/mod.rs @@ -0,0 +1,98 @@ +// Copyleft (ɔ) 2021-2021 The Whirlsplash Collective +// SPDX-License-Identifier: GPL-3.0-only + +mod builtins; +mod structure; + +use std::{io, io::Write, str::FromStr}; + +use crate::prompt::{ + builtins::{builtin_echo, BuiltIn}, + structure::Command, +}; + +pub struct Prompt; +impl Prompt { + pub fn handle() { + Prompt::write_prompt(); + Prompt::process_command(Prompt::tokenize_command(Prompt::read_command())); + } + + fn write_prompt() { + print!("> "); + io::stdout().flush().unwrap(); + } + + fn read_command() -> String { + let mut input = String::new(); + io::stdin() + .read_line(&mut input) + .expect("failed to read command from stdin"); + + 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, + } + } + + fn process_command(c: Command) -> i32 { + match BuiltIn::from_str(&c.keyword) { + Ok(BuiltIn::Echo) => builtin_echo(&c.args), + Ok(BuiltIn::Exit) => std::process::exit(0), + _ => { + println!("{}: command not found", &c.keyword); + 1 + } + } + } +} + +#[cfg(test)] +mod tokenize_command { + use crate::prompt::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() + ) + } +} diff --git a/src/prompt/structure.rs b/src/prompt/structure.rs new file mode 100644 index 0000000..b153cde --- /dev/null +++ b/src/prompt/structure.rs @@ -0,0 +1,7 @@ +// Copyleft (ɔ) 2021-2021 The Whirlsplash Collective +// SPDX-License-Identifier: GPL-3.0-only + +pub struct Command { + pub keyword: String, + pub args: Vec<String>, +} diff --git a/src/subs.rs b/src/subs.rs index df8dfd1..5e0d769 100644 --- a/src/subs.rs +++ b/src/subs.rs @@ -1,11 +1,10 @@ // Copyleft (ɔ) 2021-2021 The Whirlsplash Collective // SPDX-License-Identifier: GPL-3.0-only -use std::error::Error; - use crate::{ api::Api, config::Config, + prompt::Prompt, server::{ distributor::Distributor, hub::Hub, @@ -16,8 +15,8 @@ use crate::{ }, }; -pub async fn run() -> Result<(), Box<dyn Error>> { - let threads = vec![ +pub async fn run() -> ! { + let _threads = vec![ tokio::spawn(async move { let _ = Distributor::listen( &*format!("0.0.0.0:{}", Config::get().unwrap().distributor.port), @@ -36,9 +35,10 @@ pub async fn run() -> Result<(), Box<dyn Error>> { let _ = Api::listen(); }), ]; - for thread in threads { - let _ = thread.await; - } - Ok(()) + std::thread::sleep(std::time::Duration::from_secs(2)); + loop { + // TODO: Find a way to keep this persistent on the bottom row. + Prompt::handle(); + } } |