use core::str; use std::path::PathBuf; use std::sync::Arc; use tokio::io::AsyncReadExt; use tokio::io::AsyncWriteExt; use crate::cmd::Cmd; use crate::error::DBError; use crate::options; use crate::protocol::Protocol; use crate::storage::Storage; #[derive(Clone)] pub struct Server { pub storage: Arc, pub option: options::DBOption, } impl Server { pub async fn new(option: options::DBOption) -> Self { // Create database file path with fixed filename let db_file_path = PathBuf::from(option.dir.clone()).join("herodb.redb"); println!("will open db file path: {}", db_file_path.display()); // Initialize storage with redb let storage = Storage::new(db_file_path).expect("Failed to initialize storage"); Server { storage: Arc::new(storage), option, } } pub async fn handle( &mut self, mut stream: tokio::net::TcpStream, ) -> Result<(), DBError> { let mut buf = [0; 512]; let mut queued_cmd: Option> = None; loop { if let Ok(len) = stream.read(&mut buf).await { if len == 0 { println!("[handle] connection closed"); return Ok(()); } let s = str::from_utf8(&buf[..len])?; let (cmd, protocol) = Cmd::from(s).unwrap_or((Cmd::Unknow, Protocol::err("unknow cmd"))); println!("got command: {:?}, protocol: {:?}", cmd, protocol); let res = cmd .run(self, protocol, &mut queued_cmd) .await .unwrap_or(Protocol::err("unknow cmd")); print!("queued cmd {:?}", queued_cmd); println!("going to send response {}", res.encode()); _ = stream.write(res.encode().as_bytes()).await?; } else { println!("[handle] going to break"); break; } } Ok(()) } }