aboutsummaryrefslogtreecommitdiff
path: root/src/repl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/repl.rs')
-rw-r--r--src/repl.rs134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/repl.rs b/src/repl.rs
new file mode 100644
index 0000000..339405e
--- /dev/null
+++ b/src/repl.rs
@@ -0,0 +1,134 @@
+use crate::common::expr::Error as ProtoError;
+use crate::common::expr::Expr;
+use crate::common::term::Term;
+use std::io::BufReader;
+use std::io::Write;
+use std::net::TcpStream;
+use std::process::ExitCode;
+
+pub const USAGE: &str = "
+Usage: blom start
+
+ Starts the blom repl.
+
+ -b, --bind Bind to the given ADDRESS:PORT. Default is
+ 0.0.0.0:4902
+
+";
+
+enum Error {
+ Connect,
+}
+
+pub fn cmd(args: &[String]) -> ExitCode {
+ let mut addr = "0.0.0.0:4902";
+
+ let mut arg_index: usize = 0;
+ while arg_index < args.len() {
+ match args[arg_index].as_str() {
+ "--addr" | "-a" => {
+ addr = args
+ .get(arg_index + 1)
+ .expect("Missing address after --addr.")
+ .as_str();
+ arg_index += 1;
+ }
+ _ => {
+ println!("Too many arguments.");
+ return ExitCode::FAILURE;
+ }
+ }
+
+ arg_index += 1;
+ }
+
+ match start(addr) {
+ Ok(()) => ExitCode::SUCCESS,
+ Err(_) => ExitCode::FAILURE,
+ }
+}
+
+fn start(addr: &str) -> Result<(), Error> {
+ let mut term = Term::new();
+
+ term.setup_prompt(format!("{addr}> ").as_bytes());
+ term.setup_hints(hints);
+
+ while let Some(query) = input(&mut term) {
+ if query.is_empty() {
+ continue;
+ }
+
+ let mut stream = match try_connect(addr) {
+ Ok(stream) => stream,
+ Err(_) => continue,
+ };
+
+ let expr = Expr::from_query(&query);
+
+ if stream.write(&expr.encode()).is_err() {
+ println!("Couldn't connect to blom at {addr}");
+ continue;
+ }
+
+ let mut resp = BufReader::new(&stream);
+
+ match Expr::from_bytes(&mut resp) {
+ Ok(resp) => println!("{resp}"),
+ Err(ProtoError::ConnectionClosed) => {
+ println!("Couldn't connect to blom at {addr}");
+ continue;
+ }
+ Err(e) => {
+ println!("{e}");
+ break;
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn try_connect(addr: &str) -> Result<TcpStream, Error> {
+ match TcpStream::connect(addr) {
+ Ok(stream) => Ok(stream),
+ Err(_) => {
+ println!("Couldn't connect to blom at {addr}.");
+ Err(Error::Connect)
+ }
+ }
+}
+
+fn input(term: &mut Term) -> Option<String> {
+ let line = term.edit()?;
+ let query = std::str::from_utf8(&line).unwrap().trim();
+
+ if query == "exit" {
+ return None;
+ }
+
+ Some(query.to_string())
+}
+
+// TODO: Refactor
+fn hints(line: &[u8]) -> &[u8] {
+ match line {
+ b"ECHO" | b"echo" => b" MESSAGE",
+ b"SET" | b"set" => b" BLOCK META",
+ b"GET" | b"get" => b" BLOCK",
+ b"DEL" | b"del" => b" BLOCK",
+ b"EXISTS" | b"exists" => b" BLOCK",
+ b"INFO" | b"info" => b" BLOCK",
+ b"PARENT" | b"parent" => b" BLOCK",
+ b"CHILDREN" | b"children" => b" BLOCK",
+ b"ECHO " | b"echo " => b"MESSAGE",
+ b"SET " | b"set " => b"BLOCK META",
+ b"GET " | b"get " => b"BLOCK",
+ b"DEL " | b"del " => b"BLOCK",
+ b"EXISTS " | b"exists " => b"BLOCK",
+ b"INFO " | b"info " => b"BLOCK",
+ b"PARENT " | b"parent " => b"BLOCK",
+ b"CHILDREN " | b"children " => b"BLOCK",
+ _ => b"",
+ }
+}