apply client/cli polish (#15)
Continuation of #11. Refines the client structure and implements GET. `clap` is decoupled from the lib code. This is done to avoid any CLI parsing concerns to leak into the lib. The main motivation for this is to allow the reader to focus on Tokio concerns and not CLI parsing concerns.
This commit is contained in:
98
src/bin/cli.rs
Normal file
98
src/bin/cli.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use mini_redis::{client, DEFAULT_PORT};
|
||||
|
||||
use bytes::Bytes;
|
||||
use clap::Clap;
|
||||
use std::num::ParseIntError;
|
||||
use std::str;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "mini-redis-cli", version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"), about = "Issue Redis commands")]
|
||||
struct Cli {
|
||||
#[clap(subcommand)]
|
||||
command: Command,
|
||||
|
||||
#[clap(name = "hostname", long = "--host", default_value = "127.0.0.1")]
|
||||
host: String,
|
||||
|
||||
#[clap(name = "port", long = "--port", default_value = DEFAULT_PORT)]
|
||||
port: String,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum Command {
|
||||
/// Get the value of key.
|
||||
Get {
|
||||
/// Name of key to get
|
||||
key: String
|
||||
},
|
||||
/// Set key to hold the string value.
|
||||
Set {
|
||||
/// Name of key to set
|
||||
key: String,
|
||||
|
||||
/// Value to set.
|
||||
#[clap(parse(from_str = bytes_from_str))]
|
||||
value: Bytes,
|
||||
|
||||
/// Expire the value after specified amount of time
|
||||
#[clap(parse(try_from_str = duration_from_ms_str))]
|
||||
expires: Option<Duration>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Entry point for CLI tool.
|
||||
///
|
||||
/// The `[tokio::main]` annotation signals that the Tokio runtime should be
|
||||
/// started when the function is called. The body of the function is executed
|
||||
/// within the newly spawned runtime.
|
||||
///
|
||||
/// `basic_scheduler` is used here to avoid spawning background threads. The CLI
|
||||
/// tool use case benefits more by being lighter instead of multi-threaded.
|
||||
#[tokio::main(basic_scheduler)]
|
||||
async fn main() -> mini_redis::Result<()> {
|
||||
// Enable logging
|
||||
tracing_subscriber::fmt::try_init()?;
|
||||
|
||||
// Parse command line arguments
|
||||
let cli = Cli::parse();
|
||||
|
||||
// Get the remote address to connect to
|
||||
let addr = format!("{}:{}", cli.host, cli.port);
|
||||
|
||||
// Establish a connection
|
||||
let mut client = client::connect(&addr).await?;
|
||||
|
||||
match cli.command {
|
||||
Command::Get { key } => {
|
||||
if let Some(value) = client.get(&key).await? {
|
||||
if let Ok(string) = str::from_utf8(&value) {
|
||||
println!("\"{}\"", string);
|
||||
} else {
|
||||
println!("{:?}", value);
|
||||
}
|
||||
} else {
|
||||
println!("(nil)");
|
||||
}
|
||||
}
|
||||
Command::Set { key, value, expires: None } => {
|
||||
client.set(&key, value).await?;
|
||||
println!("OK");
|
||||
}
|
||||
Command::Set { key, value, expires: Some(expires) } => {
|
||||
client.set_expires(&key, value, expires).await?;
|
||||
println!("OK");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn duration_from_ms_str(src: &str) -> Result<Duration, ParseIntError> {
|
||||
let ms = src.parse::<u64>()?;
|
||||
Ok(Duration::from_millis(ms))
|
||||
}
|
||||
|
||||
fn bytes_from_str(src: &str) -> Bytes {
|
||||
Bytes::from(src.to_string())
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
use clap::Clap;
|
||||
use mini_redis::{client, cmd::Set, DEFAULT_PORT};
|
||||
use std::str;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let cli = Cli::parse();
|
||||
let port = cli.port.unwrap_or(DEFAULT_PORT.to_string());
|
||||
let mut client = client::connect(&format!("127.0.0.1:{}", port)).await?;
|
||||
match cli.command {
|
||||
Client::Get { key } => {
|
||||
let result = client.get(&key).await?;
|
||||
if let Some(result) = result {
|
||||
println!("\"{}\"", str::from_utf8(&result).unwrap());
|
||||
} else {
|
||||
println!("(nil)");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Client::Set(opts) => match client.set_with_opts(opts).await {
|
||||
Ok(_) => {
|
||||
println!("OK");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
Err(e)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
#[clap(name = "mini-redis-client", version = env!("CARGO_PKG_VERSION"), author = env!("CARGO_PKG_AUTHORS"), about = "Opens a connection to a Redis server")]
|
||||
struct Cli {
|
||||
#[clap(subcommand)]
|
||||
command: Client,
|
||||
#[clap(name = "port", long = "--port")]
|
||||
port: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum Client {
|
||||
/// Gets a value associated with a key
|
||||
Get { key: String },
|
||||
|
||||
/// Associates a value with a key
|
||||
Set(Set),
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use clap::Clap;
|
||||
use mini_redis::{server, DEFAULT_PORT};
|
||||
|
||||
use clap::Clap;
|
||||
|
||||
#[tokio::main]
|
||||
pub async fn main() -> Result<()> {
|
||||
pub async fn main() -> mini_redis::Result<()> {
|
||||
// enable logging
|
||||
// see https://docs.rs/tracing for more info
|
||||
tracing_subscriber::fmt::try_init().map_err(|e| anyhow!("{:?}", e))?;
|
||||
tracing_subscriber::fmt::try_init()?;
|
||||
|
||||
let cli = Cli::parse();
|
||||
let port = cli.port.unwrap_or(DEFAULT_PORT.to_string());
|
||||
|
||||
Reference in New Issue
Block a user