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 { 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 { 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"", } }