diff --git a/Cargo.lock b/Cargo.lock index be462b8..2a5cc63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -83,8 +83,10 @@ dependencies = [ "bon", "bytes", "byteyarn", + "clap", "env_logger", "log", + "shell-words", "thiserror", "tokio", ] @@ -177,6 +179,46 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "clap" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + [[package]] name = "colorchoice" version = "1.0.4" @@ -253,6 +295,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "ident_case" version = "1.0.1" @@ -500,6 +548,12 @@ dependencies = [ "syn", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "signal-hook-registry" version = "1.4.5" diff --git a/Cargo.toml b/Cargo.toml index 90200fb..53ccc06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,12 +2,24 @@ name = "archive" version = "0.1.0" edition = "2024" +authors = ["409"] +default-run = "archive-server" [dependencies] bon = "3.6.4" bytes = "1.10.1" byteyarn = "0.5.1" +clap = { version = "4.5.40", features = ["derive"] } env_logger = "0.11.8" log = "0.4.27" +shell-words = "1.1.0" thiserror = "2.0.12" tokio = { version = "1.45.1", features = ["full"] } + +[[bin]] +name = "archive-server" +path = "src/bin/server.rs" + +[[bin]] +name = "archive-cli" +path = "src/bin/cli.rs" diff --git a/src/bin/cli.rs b/src/bin/cli.rs new file mode 100644 index 0000000..e912f84 --- /dev/null +++ b/src/bin/cli.rs @@ -0,0 +1,123 @@ +use std::io::{Write as _, stdin}; + +use archive::{Result, client::Client}; +use clap::Parser; + +#[derive(Debug, Parser)] +#[command(version, about, long_about = None)] +struct Args { + #[arg(long, default_value = "127.0.0.1")] + host: String, + #[arg(short, long, default_value_t = 6171)] + port: u16, +} + +#[derive(Debug, Parser)] +#[command(help_template = "{subcommands}")] +enum Commands { + Get { + key: String, + }, + Set { + key: String, + value: String, + expiration: Option, + }, + Delete { + key: String, + }, + Has { + key: String, + }, + Ttl { + key: String, + }, + Expire { + key: String, + seconds: u64, + }, + Persist { + key: String, + }, + #[command(aliases = &["exit", "q"])] + Quit, +} + +#[tokio::main] +async fn main() -> Result<()> { + let args = Args::parse(); + + let mut client = Client::new(&format!("{}:{}", args.host, args.port)).await?; + + print_welcome(); + + let stdin = stdin(); + let mut input = String::new(); + + loop { + input.clear(); + print!("> "); + std::io::stdout().flush()?; + stdin.read_line(&mut input)?; + + let Ok(mut words) = shell_words::split(&input) else { + continue; + }; + + words.insert(0, "".into()); + + let command = match Commands::try_parse_from(words.clone()) { + Ok(command) => command, + Err(e) => { + println!("{}", e.render()); + + continue; + } + }; + + match command { + Commands::Get { key } => { + let value = client.get(&key).await?; + println!("{value:?}"); + } + Commands::Set { + key, + value, + expiration, + } => { + client.set(&key, value.as_bytes(), expiration).await?; + println!("1"); + } + Commands::Delete { key } => { + let value = client.delete(&key).await?; + println!("{value:?}"); + } + Commands::Has { key } => { + let value = client.has(&key).await?; + println!("{value:?}"); + } + Commands::Ttl { key } => { + let value = client.ttl(&key).await?; + println!("{value:?}"); + } + Commands::Expire { key, seconds } => { + let value = client.expire(&key, seconds).await?; + println!("{value:?}"); + } + Commands::Persist { key } => { + let value = client.persist(&key).await?; + println!("{value:?}"); + } + Commands::Quit => break, + } + } + + println!("{input}"); + + Ok(()) +} + +fn print_welcome() { + let version = env!("CARGO_PKG_VERSION"); + println!("archive-cli {version}"); +} diff --git a/src/main.rs b/src/bin/server.rs similarity index 81% rename from src/main.rs rename to src/bin/server.rs index 6e8a223..ae8093e 100644 --- a/src/main.rs +++ b/src/bin/server.rs @@ -1,22 +1,9 @@ -use config::ServerConfig; -use errors::AppError; -use server::Server; +use archive::Result; +use archive::config::ServerConfig; +use archive::server::Server; + use tokio::{signal::ctrl_c, sync::oneshot}; -#[cfg(test)] -pub mod tests; - -pub mod client; -pub mod commands; -pub mod config; -pub mod connection; -pub mod database; -pub mod errors; -pub mod handler; -pub mod server; - -pub type Result = std::result::Result; - #[tokio::main] async fn main() -> Result<()> { env_logger::builder() diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..bc6e8f5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,15 @@ +use errors::AppError; + +#[cfg(test)] +pub mod tests; + +pub mod client; +pub mod commands; +pub mod config; +pub mod connection; +pub mod database; +pub mod errors; +pub mod handler; +pub mod server; + +pub type Result = std::result::Result;