use std::{
io::{self, Read},
net::TcpStream,
};
use io::Write;
use crate::parse::{parse_command, parse_response};
use crate::{enums::RedisResult, response::RedisResponse};
pub struct Connection<T> {
pub host: String,
pub port: u16,
pub stream: T,
}
impl<T> Connection<T>
where
T: Read + Write,
{
pub fn new(host: &str, port: u16, stream: T) -> Connection<T> {
Connection {
host: host.to_string(),
port,
stream,
}
}
pub fn new_tcp(host: &str, port: u16) -> RedisResult<Connection<TcpStream>> {
let addr = format!("{}:{}", host, port);
let stream = TcpStream::connect(addr)?;
Ok(Connection {
host: host.to_string(),
port,
stream,
})
}
pub fn send_raw_request(&mut self, command: &str) -> RedisResult<RedisResponse> {
let request = parse_command(command)?;
let _ = self.write(&request)?;
let response = self.read()?;
let response = parse_response(&response)?;
Ok(response)
}
pub fn append(&mut self, key: &str, value: &str) -> RedisResult<RedisResponse> {
let request = format!("append {} '{}'", key, value);
let command = parse_command(&request)?;
let _ = self.write(&command)?;
let response = self.read()?;
let parsed_response = parse_response(&response)?;
Ok(parsed_response)
}
pub fn get(&mut self, key: &str) -> RedisResult<RedisResponse> {
let request = format!("get {}", &key);
let response = self.send_raw_request(&request)?;
Ok(response)
}
pub fn echo(&mut self, string: &str) -> RedisResult<RedisResponse> {
let request = format!("echo {}", &string);
let response = self.send_raw_request(&request)?;
Ok(response)
}
pub fn set(&mut self, key: &str, value: &str) -> RedisResult<RedisResponse> {
let request = format!(
"*3\r\n$3\r\nset\r\n${}\r\n{}\r\n${}\r\n{}\r\n",
key.chars().count(),
key,
value.chars().count(),
value
);
let _ = self.write(&request)?;
let response = self.read()?;
let response = parse_response(&response)?;
Ok(response)
}
pub fn ping(&mut self) -> RedisResult<RedisResponse> {
let request = "PING";
let response = self.send_raw_request(request)?;
Ok(response)
}
pub fn delete(&mut self, keys: Vec<&str>) -> RedisResult<RedisResponse> {
let mut request = String::from("del ");
for key in keys {
request.push_str(&format!("{} ", key));
}
let response = self.send_raw_request(&request)?;
Ok(response)
}
pub fn copy(&mut self, source: &str, destination: &str) -> RedisResult<RedisResponse> {
let request = format!("copy {} {}", source, destination);
let response = self.send_raw_request(&request)?;
Ok(response)
}
fn write(&mut self, request: &str) -> RedisResult<()> {
let _ = self.stream.write(request.as_bytes())?;
Ok(())
}
fn read(&mut self) -> RedisResult<String> {
let mut response = String::new();
let mut buffer: [u8; 512] = [0; 512];
let _ = self.stream.read(&mut buffer)?;
for char in buffer.iter() {
response.push(*char as char);
}
Ok(response)
}
}
#[cfg(test)]
mod test {
use std::net::TcpStream;
use crate::connection;
use crate::{enums::RedisResult, response::RedisResponse};
use super::Connection;
fn create_connection(addr: &str) -> RedisResult<TcpStream> {
Ok(TcpStream::connect(addr)?)
}
const HOST: &'static str = "127.0.0.1";
const PORT: u16 = 6379;
const ADDR: &'static str = "127.0.0.1:6379";
#[test]
fn test_passing_tcp_stream_to_connection() {
let stream = create_connection(ADDR).unwrap();
let mut client = Connection::new(HOST, PORT, stream);
let response = client.set("new", "bar").unwrap();
assert_eq!(response, RedisResponse::SimpleString(String::from("OK")));
}
#[test]
fn test_reuse_connection() {
let stream = create_connection(ADDR).unwrap();
let mut client = Connection::new(HOST, PORT, stream);
let _ = client.get("FOO").unwrap();
let _ = client.get("FOO").unwrap();
let _ = client.get("FOO").unwrap();
}
#[test]
fn test_connection_new() {
let host = "127.0.0.1";
let port = 6379;
let stream = create_connection(ADDR).unwrap();
let c = connection::Connection::new(host, port, stream);
assert_eq!(c.host, host);
assert_eq!(c.port, port);
}
#[test]
fn test_append() {
let stream = create_connection(ADDR).unwrap();
let mut client = connection::Connection::new(HOST, PORT, stream);
let _ = client.set("append_value", "value");
let response = client.append("append_value", "foo").unwrap();
assert_eq!(response, RedisResponse::Integer(8));
}
#[test]
fn test_send_get() {
let stream = create_connection(ADDR).unwrap();
let mut client = connection::Connection::new(HOST, PORT, stream);
let response = client.get("FOO").unwrap();
assert_eq!(response, RedisResponse::BulkString(String::from("BAR")));
}
#[test]
fn test_ping() {
let stream = create_connection(ADDR).unwrap();
let mut client = connection::Connection::new(HOST, PORT, stream);
let response = client.ping().unwrap();
assert_eq!(response, RedisResponse::SimpleString(String::from("PONG")));
}
#[test]
fn test_send_set() {
let stream = create_connection(ADDR).unwrap();
let mut client = connection::Connection::new(HOST, PORT, stream);
let response = client.set("BAZ", "QUUX").unwrap();
assert_eq!(response, RedisResponse::SimpleString(String::from("OK")));
}
#[test]
fn test_delete() {
let stream = create_connection(ADDR).unwrap();
let mut client = connection::Connection::new(HOST, PORT, stream);
let key = vec!["val"];
let value = "value";
let _ = client.set(key[0], value);
let response = client.delete(key).unwrap();
assert_eq!(response, RedisResponse::Integer(1));
}
#[test]
fn test_multi_delete() {
let stream = create_connection(ADDR).unwrap();
let mut client = connection::Connection::new(HOST, PORT, stream);
let _ = client.set("bar1", "bar");
let _ = client.set("bar2", "bar");
let keys = vec!["bar1", "bar2"];
let response = client.delete(keys).unwrap();
assert_eq!(response, RedisResponse::Integer(2));
}
#[test]
fn test_copy() {
let stream = create_connection(ADDR).unwrap();
let mut client = connection::Connection::new(HOST, PORT, stream);
let _ = client.set("bar1", "bar");
let _ = client.delete(vec!["new_bar"]);
let response = client.copy("bar1", "new_bar").unwrap();
let response_value = match response {
RedisResponse::Integer(x) => x,
_ => panic!(),
};
assert!(response_value <= 1);
}
#[test]
fn test_send_raw_request() {
let stream = create_connection(ADDR).unwrap();
let mut c = connection::Connection::new(HOST, PORT, stream);
let response = c.send_raw_request("PING").unwrap();
assert_eq!(response, RedisResponse::SimpleString(String::from("PONG")));
assert_eq!(
c.send_raw_request("set mynewvalue 'a great new value'")
.unwrap(),
RedisResponse::SimpleString(String::from("OK"))
);
}
#[test]
fn test_parse_send_quoted_set() {
let stream = create_connection(ADDR).unwrap();
let mut connection = connection::Connection::new(HOST, PORT, stream);
let response = connection.set("myvalue", "a custom value").unwrap();
assert_eq!(response, RedisResponse::SimpleString(String::from("OK")));
}
#[test]
fn test_connection_write() {
let stream = create_connection(ADDR).unwrap();
let mut connection = connection::Connection::new(HOST, PORT, stream);
let command = "PING\r\n";
let response = connection.write(command);
assert!(response.is_ok());
}
#[test]
fn test_connection_test_multi_word_requests() {
let stream = create_connection(ADDR).unwrap();
let mut connection = connection::Connection::new(HOST, PORT, stream);
let set_request = String::from("SET FOO BAR");
let get_request = String::from("GET FOO");
let set_response = connection.send_raw_request(&set_request);
assert!(set_response.is_ok());
let get_response = connection.send_raw_request(&get_request).unwrap();
assert_eq!(get_response, RedisResponse::BulkString(String::from("BAR")));
}
}