Update src/main.rs
This commit is contained in:
parent
05e7d5816b
commit
a531a00fe5
147
src/main.rs
147
src/main.rs
@ -1,22 +1,133 @@
|
|||||||
mod config;
|
use std::io::{self, Write};
|
||||||
mod commands;
|
use std::net::{TcpStream, ToSocketAddrs};
|
||||||
mod server;
|
use std::time::Duration;
|
||||||
mod logging;
|
use std::env;
|
||||||
|
use resp::{Value, Decoder};
|
||||||
|
use colored::*;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
use std::sync::Arc;
|
mod resp;
|
||||||
use crate::config::ServerConfig;
|
|
||||||
use log::error;
|
|
||||||
|
|
||||||
#[tokio::main]
|
const DEFAULT_PORT: u16 = 9880;
|
||||||
async fn main() -> anyhow::Result<()> {
|
const PROMPT_NAME: &str = "futriix";
|
||||||
let config = Arc::new(ServerConfig::load()?);
|
const CONNECTION_TIMEOUT: u64 = 5; // seconds
|
||||||
logging::setup_logging(&config.log_path)?;
|
|
||||||
server::print_startup_info(&config);
|
fn main() {
|
||||||
|
// Parse command line arguments
|
||||||
if let Err(e) = server::start_server(config).await {
|
let args: Vec<String> = env::args().collect();
|
||||||
error!("Server error: {}", e);
|
let mut host = "127.0.0.1".to_string();
|
||||||
return Err(e);
|
let mut port = DEFAULT_PORT;
|
||||||
|
|
||||||
|
if args.len() > 1 {
|
||||||
|
let re = Regex::new(r"^(?:([^:]+):)?([^:]+)(?::(\d+))?$").unwrap();
|
||||||
|
if let Some(caps) = re.captures(&args[1]) {
|
||||||
|
if let Some(h) = caps.get(2) {
|
||||||
|
host = h.as_str().to_string();
|
||||||
|
}
|
||||||
|
if let Some(p) = caps.get(3) {
|
||||||
|
port = p.as_str().parse().unwrap_or(DEFAULT_PORT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to connect to server
|
||||||
|
let addr = format!("{}:{}", host, port);
|
||||||
|
let socket_addr = format!("{}:{}", host, port)
|
||||||
|
.to_socket_addrs()
|
||||||
|
.expect("Failed to parse address")
|
||||||
|
.next()
|
||||||
|
.expect("No address found");
|
||||||
|
|
||||||
|
let stream = match TcpStream::connect_timeout(&socket_addr, Duration::from_secs(CONNECTION_TIMEOUT)) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to connect to {}: {}", addr, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("Connected to {}", addr);
|
||||||
|
|
||||||
|
// Main REPL loop
|
||||||
|
let mut input = String::new();
|
||||||
|
loop {
|
||||||
|
// Print prompt
|
||||||
|
print_prompt(&host, port);
|
||||||
|
|
||||||
|
// Read input
|
||||||
|
input.clear();
|
||||||
|
io::stdin().read_line(&mut input).expect("Failed to read input");
|
||||||
|
let input = input.trim();
|
||||||
|
|
||||||
|
if input.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle special commands
|
||||||
|
if input.eq_ignore_ascii_case("quit") || input.eq_ignore_ascii_case("exit") {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send command to server
|
||||||
|
match send_command(&stream, input) {
|
||||||
|
Ok(response) => {
|
||||||
|
print_response(&response);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_prompt(host: &str, port: u16) {
|
||||||
|
let prompt = format!("{}:{}:{}:~>", PROMPT_NAME, host, port);
|
||||||
|
print!("{} ", prompt.green());
|
||||||
|
io::stdout().flush().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_command(stream: &TcpStream, command: &str) -> io::Result<Value> {
|
||||||
|
// Parse command into RESP format
|
||||||
|
let parts: Vec<&str> = command.split_whitespace().collect();
|
||||||
|
let mut resp_command = String::new();
|
||||||
|
|
||||||
|
// RESP protocol: *<number of arguments>\r\n$<length of argument>\r\n<argument>\r\n...
|
||||||
|
resp_command.push_str(&format!("*{}\r\n", parts.len()));
|
||||||
|
for part in parts {
|
||||||
|
resp_command.push_str(&format!("${}\r\n{}\r\n", part.len(), part));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send command
|
||||||
|
let mut stream = stream.try_clone()?;
|
||||||
|
stream.write_all(resp_command.as_bytes())?;
|
||||||
|
|
||||||
|
// Read response
|
||||||
|
let mut decoder = Decoder::new(&stream);
|
||||||
|
decoder.decode()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_response(value: &Value) {
|
||||||
|
match value {
|
||||||
|
Value::SimpleString(s) | Value::BulkString(s) => println!("{}", s),
|
||||||
|
Value::Error(e) => println!("(error) {}", e),
|
||||||
|
Value::Integer(i) => println!("(integer) {}", i),
|
||||||
|
Value::Array(arr) => {
|
||||||
|
for (i, item) in arr.iter().enumerate() {
|
||||||
|
print!("{}) ", i + 1);
|
||||||
|
print_response(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Null => println!("(nil)"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_print_prompt() {
|
||||||
|
// This would need to be captured and checked in a real test
|
||||||
|
print_prompt("127.0.0.1", 9880);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user