From d6eaedab272c265c5fea3de136a4882edb373acf Mon Sep 17 00:00:00 2001 From: Aden-Q Date: Fri, 4 Apr 2025 15:58:37 -0500 Subject: [PATCH 1/2] style: lint --- Cargo.lock | 28 ++-- Cargo.toml | 17 +++ examples/hello_redis.rs | 13 +- src/bin/cli.rs | 14 +- src/client.rs | 37 +++--- src/cmd/decr.rs | 25 ++-- src/cmd/del.rs | 25 ++-- src/cmd/exists.rs | 25 ++-- src/cmd/expire.rs | 29 ++-- src/cmd/get.rs | 25 ++-- src/cmd/getex.rs | 62 ++++----- src/cmd/hello.rs | 32 ++--- src/cmd/incr.rs | 25 ++-- src/cmd/lpop.rs | 33 ++--- src/cmd/lpush.rs | 29 ++-- src/cmd/lrange.rs | 33 +++-- src/cmd/mod.rs | 6 +- src/cmd/ping.rs | 28 ++-- src/cmd/rpop.rs | 33 ++--- src/cmd/rpush.rs | 29 ++-- src/cmd/set.rs | 29 ++-- src/cmd/ttl.rs | 44 +++++-- src/error.rs | 4 +- src/frame.rs | 285 ++++++++++++++++++++++++++++------------ tests/integration.rs | 10 +- 25 files changed, 538 insertions(+), 382 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53cebf5..d6975dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -244,9 +244,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.17" +version = "1.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" dependencies = [ "shlex", ] @@ -272,9 +272,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.34" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -292,9 +292,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.34" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -448,9 +448,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3716d7a920fb4fac5d84e9d4bce8ceb321e9414b4409da61b07b75c1e3d0697" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" dependencies = [ "anstream", "anstyle", @@ -467,9 +467,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -1084,9 +1084,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" dependencies = [ "adler2", ] @@ -1369,9 +1369,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags 2.9.0", "errno", diff --git a/Cargo.toml b/Cargo.toml index 6249467..c2a4475 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,3 +33,20 @@ shlex = "1.3.0" assert_cmd = "2.0.16" predicates = "3.1.3" testcontainers = "0.23.3" + +[lints.clippy] +single_match = "warn" +single_match_else = "warn" +needless_match = "warn" +needless_late_init = "warn" +redundant_pattern_matching = "warn" +redundant_pattern = "warn" +redundant_guards = "warn" +collapsible_match = "warn" +match_single_binding = "warn" +match_same_arms = "warn" +match_ref_pats = "warn" +match_bool = "warn" +needless_bool = "deny" +unwrap_used = "warn" +expect_used = "warn" diff --git a/examples/hello_redis.rs b/examples/hello_redis.rs index be76d37..9cb3825 100644 --- a/examples/hello_redis.rs +++ b/examples/hello_redis.rs @@ -13,8 +13,13 @@ async fn main() -> Result<()> { for id in 0..num_clients { let handle = tokio::spawn(async move { - let mut client = Client::connect("127.0.0.1:6379").await.unwrap(); - let response = client.ping(Some("Redis".as_bytes())).await.unwrap(); + let mut client = Client::connect("127.0.0.1:6379") + .await + .unwrap_or_else(|err| panic!("Failed to connect to Redis server: {:?}", err)); + let response = client + .ping(Some("Redis".as_bytes())) + .await + .unwrap_or_else(|err| panic!("Failed to send PING command: {:?}", err)); if let Ok(string) = str::from_utf8(&response) { println!("From client {id}, got: \"{}\"", string); @@ -27,7 +32,9 @@ async fn main() -> Result<()> { } for handle in handles { - handle.await.unwrap(); + handle.await.unwrap_or_else(|err| { + panic!("Failed to join thread: {:?}", err); + }); } Ok(()) diff --git a/src/bin/cli.rs b/src/bin/cli.rs index 8013b5a..d82830d 100644 --- a/src/bin/cli.rs +++ b/src/bin/cli.rs @@ -373,7 +373,7 @@ async fn main() -> Result<()> { loop { print!("{addr}> "); // Print the prompt - io::stdout().flush().unwrap(); // Flush the buffer + io::stdout().flush()?; // Flush the buffer let mut input = String::new(); std::io::stdin().read_line(&mut input)?; @@ -383,8 +383,12 @@ async fn main() -> Result<()> { break; } - let args = split(input).unwrap(); - if args.is_empty() { + if let Some(args) = split(input) { + if args.is_empty() { + continue; + } + } else { + eprintln!("Error parsing input: {input}"); continue; } @@ -428,5 +432,7 @@ async fn main() -> Result<()> { // TODO: catch signals like Ctrl+C and Ctrl+D fn clear_screen() { print!("\x1B[2J\x1B[1;1H"); // Clears the screen and moves the cursor to the top-left - std::io::stdout().flush().unwrap(); + std::io::stdout().flush().unwrap_or_else(|_| { + eprintln!("Failed to clear screen"); + }); } diff --git a/src/client.rs b/src/client.rs index 13412e5..41a433c 100644 --- a/src/client.rs +++ b/src/client.rs @@ -65,7 +65,7 @@ impl Client { /// * `Ok(HashMap>)` if the HELLO command is successful /// * `Err(RedisError)` if an error occurs pub async fn hello(&mut self, proto: Option) -> Result>> { - let frame: Frame = Hello::new(proto).into_stream(); + let frame: Frame = Hello::new(proto).try_into()?; self.conn .write_frame(&frame) @@ -122,7 +122,7 @@ impl Client { /// } /// ``` pub async fn ping(&mut self, msg: Option<&[u8]>) -> Result> { - let frame: Frame = Ping::new(msg).into_stream(); + let frame: Frame = Ping::new(msg).try_into()?; self.conn .write_frame(&frame) @@ -168,7 +168,7 @@ impl Client { /// } /// ``` pub async fn get(&mut self, key: &str) -> Result>> { - let frame: Frame = Get::new(key).into_stream(); + let frame: Frame = Get::new(key).try_into()?; self.conn .write_frame(&frame) @@ -215,7 +215,7 @@ impl Client { /// } /// ``` pub async fn get_ex(&mut self, key: &str, expiry: Option) -> Result>> { - let frame: Frame = GetEx::new(key, expiry).into_stream(); + let frame: Frame = GetEx::new(key, expiry).try_into()?; self.conn.write_frame(&frame).await?; @@ -272,7 +272,7 @@ impl Client { /// let resp = client.set("mykey", "myvalue").await?; /// } pub async fn set(&mut self, key: &str, val: &[u8]) -> Result>> { - let frame: Frame = Set::new(key, val).into_stream(); + let frame: Frame = Set::new(key, val).try_into()?; self.conn .write_frame(&frame) @@ -349,7 +349,7 @@ impl Client { /// let resp = client.del(vec!["foo", "bar", "baz"]).await?; /// } pub async fn del(&mut self, keys: Vec<&str>) -> Result { - let frame: Frame = Del::new(keys).into_stream(); + let frame: Frame = Del::new(keys).try_into()?; self.conn .write_frame(&frame) @@ -390,7 +390,7 @@ impl Client { /// let resp = client.exists(vec!["foo", "bar", "baz"]).await?; /// } pub async fn exists(&mut self, keys: Vec<&str>) -> Result { - let frame: Frame = Exists::new(keys).into_stream(); + let frame: Frame = Exists::new(keys).try_into()?; self.conn .write_frame(&frame) @@ -434,7 +434,7 @@ impl Client { /// let resp = client.expire("mykey", 1).await?; /// } pub async fn expire(&mut self, key: &str, seconds: i64) -> Result { - let frame: Frame = Expire::new(key, seconds).into_stream(); + let frame: Frame = Expire::new(key, seconds).try_into()?; self.conn .write_frame(&frame) @@ -477,7 +477,7 @@ impl Client { /// let resp = client.ttl("mykey").await?; /// } pub async fn ttl(&mut self, key: &str) -> Result { - let frame: Frame = Ttl::new(key).into_stream(); + let frame: Frame = Ttl::new(key).try_into()?; self.conn .write_frame(&frame) @@ -519,7 +519,7 @@ impl Client { /// let resp = client.incr("mykey").await?; /// } pub async fn incr(&mut self, key: &str) -> Result { - let frame: Frame = Incr::new(key).into_stream(); + let frame: Frame = Incr::new(key).try_into()?; self.conn .write_frame(&frame) @@ -591,7 +591,7 @@ impl Client { /// let resp = client.decr("mykey").await?; /// } pub async fn decr(&mut self, key: &str) -> Result { - let frame: Frame = Decr::new(key).into_stream(); + let frame: Frame = Decr::new(key).try_into()?; self.conn .write_frame(&frame) @@ -664,7 +664,7 @@ impl Client { /// let resp = client.lpush("mykey", vec!["foo", "bar", "baz"]).await?; /// } pub async fn lpush(&mut self, key: &str, values: Vec<&[u8]>) -> Result { - let frame: Frame = LPush::new(key, values).into_stream(); + let frame: Frame = LPush::new(key, values).try_into()?; self.conn .write_frame(&frame) @@ -706,7 +706,7 @@ impl Client { /// let resp = client.rpush("mykey", vec!["foo", "bar", "baz"]).await?; /// } pub async fn rpush(&mut self, key: &str, values: Vec<&[u8]>) -> Result { - let frame: Frame = RPush::new(key, values).into_stream(); + let frame: Frame = RPush::new(key, values).try_into()?; self.conn .write_frame(&frame) @@ -750,7 +750,7 @@ impl Client { /// let resp = client.lpop("mykey", 1).await?; /// } pub async fn lpop(&mut self, key: &str) -> Result>> { - let frame: Frame = LPop::new(key, None).into_stream(); + let frame: Frame = LPop::new(key, None).try_into()?; self.conn .write_frame(&frame) @@ -770,7 +770,7 @@ impl Client { } pub async fn lpop_n(&mut self, key: &str, count: u64) -> Result>>> { - let frame: Frame = LPop::new(key, Some(count)).into_stream(); + let frame: Frame = LPop::new(key, Some(count)).try_into()?; self.conn .write_frame(&frame) @@ -815,7 +815,7 @@ impl Client { /// let resp = client.rpop("mykey", 1).await?; /// } pub async fn rpop(&mut self, key: &str) -> Result>> { - let frame: Frame = RPop::new(key, None).into_stream(); + let frame: Frame = RPop::new(key, None).try_into()?; self.conn .write_frame(&frame) @@ -835,7 +835,7 @@ impl Client { } pub async fn rpop_n(&mut self, key: &str, count: u64) -> Result>>> { - let frame: Frame = RPop::new(key, Some(count)).into_stream(); + let frame: Frame = RPop::new(key, Some(count)).try_into()?; self.conn .write_frame(&frame) @@ -881,7 +881,7 @@ impl Client { /// let resp = client.lrange("mykey", 0, -1).await?; /// } pub async fn lrange(&mut self, key: &str, start: i64, end: i64) -> Result>> { - let frame: Frame = LRange::new(key, start, end).into_stream(); + let frame: Frame = LRange::new(key, start, end).try_into()?; self.conn .write_frame(&frame) @@ -1363,7 +1363,6 @@ impl Client { .collect::>(); result.concat() } - Frame::Null => vec![], _ => vec![], }) .collect(); diff --git a/src/cmd/decr.rs b/src/cmd/decr.rs index 616e172..9667be0 100644 --- a/src/cmd/decr.rs +++ b/src/cmd/decr.rs @@ -1,6 +1,5 @@ /// A Redis DECR command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct Decr { @@ -30,17 +29,17 @@ impl Decr { } } -impl Command for Decr { - fn into_stream(self) -> Frame { +impl Command for Decr {} + +impl TryInto for Decr { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("DECR".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("DECR".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; - frame + Ok(frame) } } @@ -51,7 +50,9 @@ mod tests { #[test] fn test_decr() { let decr = Decr::new("mykey"); - let frame = decr.into_stream(); + let frame: Frame = decr + .try_into() + .unwrap_or_else(|err| panic!("Failed to create DECR command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/del.rs b/src/cmd/del.rs index eca06b3..a6ace76 100644 --- a/src/cmd/del.rs +++ b/src/cmd/del.rs @@ -1,6 +1,5 @@ /// A Redis DEL command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct Del { @@ -30,20 +29,20 @@ impl Del { } } -impl Command for Del { - fn into_stream(self) -> Frame { +impl Command for Del {} + +impl TryInto for Del { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("DEL".into())) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("DEL".into()))?; for key in self.keys { - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString(Bytes::from(key)))?; } - frame + Ok(frame) } } @@ -54,7 +53,9 @@ mod tests { #[test] fn test_del() { let del = Del::new(vec!["key1", "key2"]); - let frame = del.into_stream(); + let frame: Frame = del + .try_into() + .unwrap_or_else(|err| panic!("Failed to create DEL command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/exists.rs b/src/cmd/exists.rs index 27ca16b..612f710 100644 --- a/src/cmd/exists.rs +++ b/src/cmd/exists.rs @@ -1,6 +1,5 @@ /// A Redis EXISTS command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct Exists { @@ -30,20 +29,20 @@ impl Exists { } } -impl Command for Exists { - fn into_stream(self) -> Frame { +impl Command for Exists {} + +impl TryInto for Exists { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("EXISTS".into())) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("EXISTS".into()))?; for key in self.keys { - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString(Bytes::from(key)))?; } - frame + Ok(frame) } } @@ -54,7 +53,9 @@ mod tests { #[test] fn test_exists() { let exists = Exists::new(vec!["key1", "key2"]); - let frame = exists.into_stream(); + let frame: Frame = exists + .try_into() + .unwrap_or_else(|err| panic!("Failed to create EXISTS command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/expire.rs b/src/cmd/expire.rs index ec0d77d..d4946d9 100644 --- a/src/cmd/expire.rs +++ b/src/cmd/expire.rs @@ -1,6 +1,5 @@ /// A Redis EXPIRE command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct Expire { @@ -33,20 +32,18 @@ impl Expire { } } -impl Command for Expire { - fn into_stream(self) -> Frame { +impl Command for Expire {} + +impl TryInto for Expire { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("EXPIRE".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.seconds.to_string()))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("EXPIRE".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.seconds.to_string())))?; - frame + Ok(frame) } } @@ -57,7 +54,9 @@ mod tests { #[test] fn test_expire() { let expire = Expire::new("mykey", 60); - let frame = expire.into_stream(); + let frame: Frame = expire + .try_into() + .unwrap_or_else(|err| panic!("Failed to create EXPIRE command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/get.rs b/src/cmd/get.rs index 5d42aea..1d4314c 100644 --- a/src/cmd/get.rs +++ b/src/cmd/get.rs @@ -1,6 +1,5 @@ /// A Redis GET command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct Get { @@ -30,17 +29,17 @@ impl Get { } } -impl Command for Get { - fn into_stream(self) -> Frame { +impl Command for Get {} + +impl TryInto for Get { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("GET".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("GET".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; - frame + Ok(frame) } } @@ -51,7 +50,9 @@ mod tests { #[test] fn test_get() { let get = Get::new("mykey"); - let frame = get.into_stream(); + let frame: Frame = get + .try_into() + .unwrap_or_else(|err| panic!("Failed to create GET command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/getex.rs b/src/cmd/getex.rs index 728a582..f3806f0 100644 --- a/src/cmd/getex.rs +++ b/src/cmd/getex.rs @@ -1,6 +1,5 @@ /// A Redis GETEX command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; #[derive(Debug)] @@ -43,58 +42,41 @@ impl GetEx { } } -impl Command for GetEx { - fn into_stream(self) -> Frame { +impl Command for GetEx {} + +impl TryInto for GetEx { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("GETEX".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("GETEX".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; if let Some(expiry) = self.expiry { match expiry { Expiry::EX(seconds) => { - frame - .push_frame_to_array(Frame::BulkString("EX".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::Integer(seconds as i64)) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("EX".into()))?; + frame.push_frame_to_array(Frame::Integer(seconds as i64))?; } Expiry::PX(milliseconds) => { - frame - .push_frame_to_array(Frame::BulkString("PX".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::Integer(milliseconds as i64)) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("PX".into()))?; + frame.push_frame_to_array(Frame::Integer(milliseconds as i64))?; } Expiry::EXAT(timestamp) => { - frame - .push_frame_to_array(Frame::BulkString("EXAT".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::Integer(timestamp as i64)) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("EXAT".into()))?; + frame.push_frame_to_array(Frame::Integer(timestamp as i64))?; } Expiry::PXAT(timestamp) => { - frame - .push_frame_to_array(Frame::BulkString("PXAT".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::Integer(timestamp as i64)) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("PXAT".into()))?; + frame.push_frame_to_array(Frame::Integer(timestamp as i64))?; } Expiry::PERSIST => { - frame - .push_frame_to_array(Frame::BulkString("PERSIST".into())) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("PERSIST".into()))?; } } } - frame + + Ok(frame) } } @@ -105,7 +87,9 @@ mod tests { #[test] fn test_get() { let getex = GetEx::new("mykey", None); - let frame = getex.into_stream(); + let frame: Frame = getex + .try_into() + .unwrap_or_else(|err| panic!("Failed to create GETEX command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/hello.rs b/src/cmd/hello.rs index 0656923..afdec54 100644 --- a/src/cmd/hello.rs +++ b/src/cmd/hello.rs @@ -1,6 +1,5 @@ /// A Redis HELLO command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; pub struct Hello { proto: Option, @@ -27,22 +26,21 @@ impl Hello { } } -impl Command for Hello { - /// Converts the Hello command into a Frame to be transimitted over the stream. - fn into_stream(self) -> Frame { +impl Command for Hello {} + +impl TryInto for Hello { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("HELLO".into())) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("HELLO".into()))?; // do not push the message if it is None if let Some(proto) = self.proto { - frame - .push_frame_to_array(Frame::BulkString(proto.to_string().into())) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString(proto.to_string().into()))?; } - frame + Ok(frame) } } @@ -53,12 +51,16 @@ mod tests { #[test] fn test_hello() { let hello = Hello::new(None); - let frame = hello.into_stream(); + let frame: Frame = hello + .try_into() + .unwrap_or_else(|err| panic!("Failed to create HELLO command: {:?}", err)); assert_eq!(frame, Frame::Array(vec![Frame::BulkString("HELLO".into())])); - let ping = Hello::new(Some(3)); - let frame = ping.into_stream(); + let hello = Hello::new(Some(3)); + let frame: Frame = hello + .try_into() + .unwrap_or_else(|err| panic!("Failed to create HELLO command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/incr.rs b/src/cmd/incr.rs index 479ba15..6d4e345 100644 --- a/src/cmd/incr.rs +++ b/src/cmd/incr.rs @@ -1,6 +1,5 @@ /// A Redis INCR command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct Incr { @@ -30,17 +29,17 @@ impl Incr { } } -impl Command for Incr { - fn into_stream(self) -> Frame { +impl Command for Incr {} + +impl TryInto for Incr { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("INCR".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("INCR".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; - frame + Ok(frame) } } @@ -51,7 +50,9 @@ mod tests { #[test] fn test_incr() { let incr = Incr::new("mykey"); - let frame = incr.into_stream(); + let frame: Frame = incr + .try_into() + .unwrap_or_else(|err| panic!("Failed to create INCR command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/lpop.rs b/src/cmd/lpop.rs index d7a1d3b..22194e6 100644 --- a/src/cmd/lpop.rs +++ b/src/cmd/lpop.rs @@ -1,6 +1,5 @@ /// A Redis LPOP command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct LPop { @@ -17,23 +16,21 @@ impl LPop { } } -impl Command for LPop { - fn into_stream(self) -> Frame { +impl Command for LPop {} + +impl TryInto for LPop { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("LPOP".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("LPOP".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; if let Some(count) = self.count { - frame - .push_frame_to_array(Frame::Integer(count as i64)) - .unwrap(); + frame.push_frame_to_array(Frame::Integer(count as i64))?; } - frame + Ok(frame) } } @@ -44,7 +41,9 @@ mod tests { #[test] fn test_lpop() { let lpop = LPop::new("mylist", None); - let frame = lpop.into_stream(); + let frame: Frame = lpop + .try_into() + .unwrap_or_else(|err| panic!("Failed to create LPOP command: {:?}", err)); assert_eq!( frame, @@ -55,7 +54,9 @@ mod tests { ); let lpop = LPop::new("mylist", Some(2)); - let frame = lpop.into_stream(); + let frame: Frame = lpop + .try_into() + .unwrap_or_else(|err| panic!("Failed to create LPOP command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/lpush.rs b/src/cmd/lpush.rs index f9d7d2b..332224f 100644 --- a/src/cmd/lpush.rs +++ b/src/cmd/lpush.rs @@ -1,6 +1,5 @@ /// A Redis LPUSH command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct LPush { @@ -33,23 +32,21 @@ impl LPush { } } -impl Command for LPush { - fn into_stream(self) -> Frame { +impl Command for LPush {} + +impl TryInto for LPush { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("LPUSH".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("LPUSH".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; for value in self.values { - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(value))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString(Bytes::from(value)))?; } - frame + Ok(frame) } } @@ -60,7 +57,9 @@ mod tests { #[test] fn test_lpush() { let lpush = LPush::new("mylist", vec![b"value1", b"value2"]); - let frame = lpush.into_stream(); + let frame: Frame = lpush + .try_into() + .unwrap_or_else(|err| panic!("Failed to create LPUSH command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/lrange.rs b/src/cmd/lrange.rs index 8fee640..3ae20ab 100644 --- a/src/cmd/lrange.rs +++ b/src/cmd/lrange.rs @@ -1,6 +1,5 @@ /// A Redis LRANGE command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct LRange { @@ -19,21 +18,19 @@ impl LRange { } } -impl Command for LRange { - fn into_stream(self) -> Frame { +impl Command for LRange {} + +impl TryInto for LRange { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("LRANGE".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); - frame - .push_frame_to_array(Frame::Integer(self.start)) - .unwrap(); - frame.push_frame_to_array(Frame::Integer(self.end)).unwrap(); - - frame + frame.push_frame_to_array(Frame::BulkString("LRANGE".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; + frame.push_frame_to_array(Frame::Integer(self.start))?; + frame.push_frame_to_array(Frame::Integer(self.end))?; + + Ok(frame) } } @@ -44,7 +41,9 @@ mod tests { #[test] fn test_lrange() { let lrange = LRange::new("mylist", 0, -1); - let frame = lrange.into_stream(); + let frame: Frame = lrange + .try_into() + .unwrap_or_else(|err| panic!("Failed to create LRANGE command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index ab02d5d..2583331 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -56,7 +56,5 @@ mod subscribe; mod unsubscribe; /// A trait for all Redis commands. -pub trait Command { - /// Converts the command into a Frame to be transimitted over the stream. - fn into_stream(self) -> Frame; -} +#[allow(unused)] +pub trait Command: TryInto {} diff --git a/src/cmd/ping.rs b/src/cmd/ping.rs index 2133177..c8300dd 100644 --- a/src/cmd/ping.rs +++ b/src/cmd/ping.rs @@ -1,6 +1,5 @@ /// A Redis PING command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct Ping { @@ -30,20 +29,21 @@ impl Ping { } } -impl Command for Ping { - /// Converts the ping command into a Frame to be transimitted over the stream. - fn into_stream(self) -> Frame { +impl Command for Ping {} + +impl TryInto for Ping { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("PING".into())) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("PING".into()))?; // do not push the message if it is None if let Some(msg) = self.msg { - frame.push_frame_to_array(Frame::BulkString(msg)).unwrap(); + frame.push_frame_to_array(Frame::BulkString(msg))?; } - frame + Ok(frame) } } @@ -54,12 +54,16 @@ mod tests { #[test] fn test_ping() { let ping = Ping::new(None); - let frame = ping.into_stream(); + let frame: Frame = ping + .try_into() + .unwrap_or_else(|err| panic!("Failed to create PING command: {:?}", err)); assert_eq!(frame, Frame::Array(vec![Frame::BulkString("PING".into())])); let ping = Ping::new(Some("hello".as_bytes())); - let frame = ping.into_stream(); + let frame: Frame = ping + .try_into() + .unwrap_or_else(|err| panic!("Failed to create PING command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/rpop.rs b/src/cmd/rpop.rs index c7b6284..7b80649 100644 --- a/src/cmd/rpop.rs +++ b/src/cmd/rpop.rs @@ -1,6 +1,5 @@ /// A Redis RPOP command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct RPop { @@ -17,22 +16,20 @@ impl RPop { } } -impl Command for RPop { - fn into_stream(self) -> Frame { +impl Command for RPop {} + +impl TryInto for RPop { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("RPOP".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("RPOP".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; if let Some(count) = self.count { - frame - .push_frame_to_array(Frame::Integer(count as i64)) - .unwrap(); + frame.push_frame_to_array(Frame::Integer(count as i64))?; } - frame + Ok(frame) } } @@ -43,7 +40,9 @@ mod tests { #[test] fn test_rpop() { let rpop = RPop::new("mylist", None); - let frame = rpop.into_stream(); + let frame: Frame = rpop + .try_into() + .unwrap_or_else(|err| panic!("Failed to create RPOP command: {:?}", err)); assert_eq!( frame, @@ -54,7 +53,9 @@ mod tests { ); let rpop = RPop::new("mylist", Some(2)); - let frame = rpop.into_stream(); + let frame: Frame = rpop + .try_into() + .unwrap_or_else(|err| panic!("Failed to create RPOP command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/rpush.rs b/src/cmd/rpush.rs index 00acb2a..c3b1131 100644 --- a/src/cmd/rpush.rs +++ b/src/cmd/rpush.rs @@ -1,6 +1,5 @@ /// A Redis RPUSH command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct RPush { @@ -33,23 +32,21 @@ impl RPush { } } -impl Command for RPush { - fn into_stream(self) -> Frame { +impl Command for RPush {} + +impl TryInto for RPush { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("RPUSH".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("RPUSH".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; for value in self.values { - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(value))) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString(Bytes::from(value)))?; } - frame + Ok(frame) } } @@ -60,7 +57,9 @@ mod tests { #[test] fn test_rpush() { let rpush = RPush::new("mylist", vec!["value1".as_bytes(), "value2".as_bytes()]); - let frame = rpush.into_stream(); + let frame: Frame = rpush + .try_into() + .unwrap_or_else(|err| panic!("Failed to create RPUSH command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/set.rs b/src/cmd/set.rs index 76dc102..3f2df03 100644 --- a/src/cmd/set.rs +++ b/src/cmd/set.rs @@ -1,6 +1,5 @@ /// A Redis SET command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; /// A Redis SET command. @@ -36,20 +35,18 @@ impl Set { } } -impl Command for Set { - fn into_stream(self) -> Frame { +impl Command for Set {} + +impl TryInto for Set { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("SET".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(self.value)) - .unwrap(); + frame.push_frame_to_array(Frame::BulkString("SET".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; + frame.push_frame_to_array(Frame::BulkString(self.value))?; - frame + Ok(frame) } } @@ -60,7 +57,9 @@ mod tests { #[test] fn test_set() { let set = Set::new("mykey", "myvalue".as_bytes()); - let frame = set.into_stream(); + let frame: Frame = set + .try_into() + .unwrap_or_else(|err| panic!("Failed to create SET command: {:?}", err)); assert_eq!( frame, diff --git a/src/cmd/ttl.rs b/src/cmd/ttl.rs index 5c27ca4..23e11c1 100644 --- a/src/cmd/ttl.rs +++ b/src/cmd/ttl.rs @@ -1,6 +1,5 @@ /// A Redis TTL command. -use crate::cmd::Command; -use crate::frame::Frame; +use crate::{Result, cmd::Command, frame::Frame}; use bytes::Bytes; pub struct Ttl { @@ -30,16 +29,37 @@ impl Ttl { } } -impl Command for Ttl { - fn into_stream(self) -> Frame { +impl Command for Ttl {} + +impl TryInto for Ttl { + type Error = crate::RedisError; + + fn try_into(self) -> Result { let mut frame: Frame = Frame::array(); - frame - .push_frame_to_array(Frame::BulkString("TTL".into())) - .unwrap(); - frame - .push_frame_to_array(Frame::BulkString(Bytes::from(self.key))) - .unwrap(); - - frame + frame.push_frame_to_array(Frame::BulkString("TTL".into()))?; + frame.push_frame_to_array(Frame::BulkString(Bytes::from(self.key)))?; + + Ok(frame) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ttl() { + let ttl = Ttl::new("mykey"); + let frame: Frame = ttl + .try_into() + .unwrap_or_else(|err| panic!("Failed to create TTL command: {:?}", err)); + + assert_eq!( + frame, + Frame::Array(vec![ + Frame::BulkString("TTL".into()), + Frame::BulkString("mykey".into()), + ]) + ); } } diff --git a/src/error.rs b/src/error.rs index 0e4d9e5..4a68912 100644 --- a/src/error.rs +++ b/src/error.rs @@ -16,8 +16,10 @@ pub enum RedisError { #[error("utf8 error")] Utf8(#[from] std::str::Utf8Error), /// So that we can use `?` operator to convert from `std::num::ParseIntError` - #[error("parseint error")] + #[error("ParseIntError")] ParseInt(#[from] std::num::ParseIntError), + #[error("TryFromIntError")] + TryFromInt(#[from] std::num::TryFromIntError), #[error("unexpected response type")] UnexpectedResponseType, /// All other errors are converted to anyhow::Error diff --git a/src/frame.rs b/src/frame.rs index 6fedc7f..7185a5b 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -52,11 +52,7 @@ impl Frame { /// This method will panic if the Frame is not an Array or Set. pub fn push_frame_to_array(&mut self, frame: Frame) -> Result<()> { match self { - Frame::Array(vec) => { - vec.push(frame); - Ok(()) - } - Frame::Set(vec) => { + Frame::Array(vec) | Frame::Set(vec) => { vec.push(frame); Ok(()) } @@ -313,7 +309,7 @@ impl Frame { b'+' => { // Simple string let mut buf = String::new(); - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; if buf.ends_with("\r\n") { Ok(Frame::SimpleString( @@ -328,7 +324,7 @@ impl Frame { b'-' => { // Simple error let mut buf = String::new(); - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; if buf.ends_with("\r\n") { Ok(Frame::SimpleError(buf.trim_end_matches("\r\n").to_string())) @@ -341,13 +337,11 @@ impl Frame { b':' => { // Integer let mut buf = String::new(); - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; // todo: check whether it is a valid integer if buf.ends_with("\r\n") { - Ok(Frame::Integer( - buf.trim_end_matches("\r\n").parse::().unwrap(), - )) + Ok(Frame::Integer(buf.trim_end_matches("\r\n").parse::()?)) } else { Err(RedisError::IncompleteFrame) } @@ -356,13 +350,13 @@ impl Frame { // Bulk string let mut buf = String::new(); // read the length of the bulk string - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; if !buf.ends_with("\r\n") { return Err(RedisError::IncompleteFrame); } - let len: isize = buf.trim_end_matches("\r\n").parse::().unwrap(); + let len: isize = buf.trim_end_matches("\r\n").parse::()?; // for RESP2, -1 indicates a null bulk string if len == -1 { @@ -384,9 +378,9 @@ impl Frame { b'*' => { // Array let mut buf = String::new(); - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; - let len = buf.trim_end_matches("\r\n").parse::().unwrap(); + let len = buf.trim_end_matches("\r\n").parse::()?; let mut frame_vec: Vec<_> = Vec::with_capacity(len); for _ in 0..len { @@ -399,7 +393,7 @@ impl Frame { b'#' => { // Boolean let mut buf = String::new(); - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; if buf.ends_with("\r\n") { let val = buf.trim_end_matches("\r\n"); @@ -417,7 +411,7 @@ impl Frame { b',' => { // Double let mut buf = String::new(); - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; if buf.ends_with("\r\n") { let val = buf.trim_end_matches("\r\n"); @@ -428,7 +422,9 @@ impl Frame { } else if val == "-inf" { Ok(Frame::Double(f64::NEG_INFINITY)) } else { - Ok(Frame::Double(val.parse::().unwrap())) + Ok(Frame::Double( + val.parse::().map_err(|_| RedisError::InvalidFrame)?, + )) } } else { Err(RedisError::IncompleteFrame) @@ -442,20 +438,20 @@ impl Frame { // Bulk error let mut buf = String::new(); // read the length of the bulk string - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; if !buf.ends_with("\r\n") { return Err(RedisError::IncompleteFrame); } - let len: isize = buf.trim_end_matches("\r\n").parse::().unwrap(); + let len: isize = buf.trim_end_matches("\r\n").parse::()?; // for RESP2, -1 indicates a null bulk error if len == -1 { return Ok(Frame::Null); } - let len: usize = len.try_into().unwrap(); + let len: usize = len.try_into()?; // +2 because \r\n if cursor.remaining() < len + 2 { @@ -478,13 +474,13 @@ impl Frame { // Verbatim string let mut buf = String::new(); // read the length of the bulk string - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; if !buf.ends_with("\r\n") { return Err(RedisError::IncompleteFrame); } - let len: usize = buf.trim_end_matches("\r\n").parse::().unwrap(); + let len: usize = buf.trim_end_matches("\r\n").parse::()?; // +2 for \r\n if cursor.remaining() < len + 2 { @@ -513,9 +509,9 @@ impl Frame { b'%' => { // Map let mut buf = String::new(); - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; - let len = buf.trim_end_matches("\r\n").parse::().unwrap(); + let len = buf.trim_end_matches("\r\n").parse::()?; let mut frame_vec: Vec<_> = Vec::with_capacity(len); for _ in 0..len { @@ -533,9 +529,9 @@ impl Frame { b'~' => { // Set let mut buf = String::new(); - let _ = cursor.read_line(&mut buf).unwrap(); + cursor.read_line(&mut buf)?; - let len = buf.trim_end_matches("\r\n").parse::().unwrap(); + let len = buf.trim_end_matches("\r\n").parse::()?; let mut frame_vec: Vec<_> = Vec::with_capacity(len); for _ in 0..len { @@ -561,7 +557,10 @@ mod tests { #[tokio::test] async fn test_serialize_simple_string() { let frame = Frame::SimpleString("OK".to_string()); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize simple string frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"+OK\r\n")); } @@ -570,7 +569,10 @@ mod tests { #[tokio::test] async fn test_serialize_simple_error() { let frame = Frame::SimpleError("ERR".to_string()); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize simple error frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"-ERR\r\n")); } @@ -580,13 +582,19 @@ mod tests { async fn test_serialize_integer() { // positive integer let frame = Frame::Integer(123_i64); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize integer frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b":123\r\n")); // negative integer let frame = Frame::Integer(-123_i64); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize integer frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b":-123\r\n")); } @@ -595,13 +603,19 @@ mod tests { #[tokio::test] async fn test_serialize_bulk_string() { let frame = Frame::BulkString(Bytes::from_static(b"Hello Redis")); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize bulk string frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"$11\r\nHello Redis\r\n")); // empty bulk string let frame = Frame::BulkString(Bytes::from_static(b"")); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize empty bulk string frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"$0\r\n\r\n")); } @@ -612,12 +626,15 @@ mod tests { let mut frame = Frame::array(); frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Hello"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to serialize array frame: {:?}", err)); frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Redis"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to serialize array frame: {:?}", err)); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize array frame: {:?}", err)); assert_eq!( bytes, @@ -626,7 +643,10 @@ mod tests { // empty array let frame = Frame::array(); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize empty array frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"*0\r\n")); @@ -635,16 +655,19 @@ mod tests { let mut nested_frame = Frame::array(); nested_frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Hello"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to serialize nested array frame: {:?}", err)); nested_frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Redis"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to serialize nested array frame: {:?}", err)); if let Frame::Array(vec) = &mut frame { vec.push(nested_frame); } - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize nested array frame: {:?}", err)); assert_eq!( bytes, @@ -656,7 +679,10 @@ mod tests { #[tokio::test] async fn test_serialize_null() { let frame = Frame::Null; - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize null frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"_\r\n")); } @@ -665,12 +691,18 @@ mod tests { #[tokio::test] async fn test_serialize_boolean() { let frame = Frame::Boolean(true); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize boolean frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"#t\r\n")); let frame = Frame::Boolean(false); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize boolean frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"#f\r\n")); } @@ -679,22 +711,34 @@ mod tests { #[tokio::test] async fn test_serialize_double() { let frame = Frame::Double(123.456); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize double frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b",123.456\r\n")); let frame = Frame::Double(f64::NAN); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize NaN frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b",nan\r\n")); let frame = Frame::Double(f64::INFINITY); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize infinity frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b",inf\r\n")); let frame = Frame::Double(f64::NEG_INFINITY); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize negative infinity frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b",-inf\r\n")); } @@ -703,13 +747,19 @@ mod tests { #[tokio::test] async fn test_serialize_bulk_error() { let frame = Frame::BulkError(Bytes::from_static(b"Hello Redis")); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize bulk error frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"!11\r\nHello Redis\r\n")); // empty bulk error let frame = Frame::BulkError(Bytes::from_static(b"")); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize empty bulk error frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"!0\r\n\r\n")); } @@ -721,13 +771,18 @@ mod tests { Bytes::from_static(b"txt"), Bytes::from_static(b"Some string"), ); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize verbatim string frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"=15\r\ntxt:Some string\r\n")); // empty verbatim string let frame = Frame::VerbatimString(Bytes::from_static(b"txt"), Bytes::from_static(b"")); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame.serialize().await.unwrap_or_else(|err| { + panic!("Failed to serialize empty verbatim string frame: {:?}", err) + }); assert_eq!(bytes, Bytes::from_static(b"=4\r\ntxt:\r\n")); } @@ -741,9 +796,12 @@ mod tests { Frame::SimpleString("key".to_string()), Frame::SimpleString("value".to_string()), ) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to serialize map frame: {:?}", err)); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize map frame: {:?}", err)); assert_eq!(bytes, Bytes::from_static(b"%1\r\n+key\r\n+value\r\n")); } @@ -754,12 +812,15 @@ mod tests { let mut frame: Frame = Frame::Set(Vec::new()); frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Hello"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to serialize set frame: {:?}", err)); frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Redis"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to serialize set frame: {:?}", err)); - let bytes = frame.serialize().await.unwrap(); + let bytes = frame + .serialize() + .await + .unwrap_or_else(|err| panic!("Failed to serialize set frame: {:?}", err)); assert_eq!( bytes, @@ -772,7 +833,9 @@ mod tests { async fn test_deserialize_simple_string() { let bytes = Bytes::from_static(b"+OK\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize simple string frame: {:?}", err)); assert_eq!(frame, Frame::SimpleString("OK".to_string())); } @@ -782,7 +845,9 @@ mod tests { async fn test_deserialize_simple_error() { let bytes = Bytes::from_static(b"-ERR\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize simple error frame: {:?}", err)); assert_eq!(frame, Frame::SimpleError("ERR".to_string())); } @@ -793,14 +858,18 @@ mod tests { // positive integer let bytes = Bytes::from_static(b":123\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize integer frame: {:?}", err)); assert_eq!(frame, Frame::Integer(123_i64)); // negative integer let bytes = Bytes::from_static(b":-123\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes).await.unwrap_or_else(|err| { + panic!("Failed to deserialize negative integer frame: {:?}", err) + }); assert_eq!(frame, Frame::Integer(-123_i64)); } @@ -810,13 +879,17 @@ mod tests { async fn test_deserialize_bulk_string() { let bytes = Bytes::from_static(b"$11\r\nHello Redis\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize bulk string frame: {:?}", err)); assert_eq!(frame, Frame::BulkString(Bytes::from_static(b"Hello Redis"))); let bytes = Bytes::from_static(b"$0\r\n\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes).await.unwrap_or_else(|err| { + panic!("Failed to deserialize empty bulk string frame: {:?}", err) + }); assert_eq!(frame, Frame::BulkString(Bytes::from_static(b""))); } @@ -826,40 +899,48 @@ mod tests { async fn test_deserialize_array() { let bytes = Bytes::from_static(b"*2\r\n$5\r\nHello\r\n$5\r\nRedis\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize array frame: {:?}", err)); let mut expected_frame = Frame::array(); expected_frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Hello"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to deserialize array frame: {:?}", err)); expected_frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Redis"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to deserialize array frame: {:?}", err)); assert_eq!(frame, expected_frame); // empty array let bytes = Bytes::from_static(b"*0\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize empty array frame: {:?}", err)); assert_eq!(frame, Frame::array()); // nested array let bytes = Bytes::from_static(b"*1\r\n*2\r\n$5\r\nHello\r\n$5\r\nRedis\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize nested array frame: {:?}", err)); let mut expected_frame = Frame::array(); let mut nested_frame = Frame::array(); nested_frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Hello"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to deserialize nested array frame: {:?}", err)); nested_frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Redis"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to deserialize nested array frame: {:?}", err)); - expected_frame.push_frame_to_array(nested_frame).unwrap(); + expected_frame + .push_frame_to_array(nested_frame) + .unwrap_or_else(|err| panic!("Failed to deserialize nested array frame: {:?}", err)); assert_eq!(frame, expected_frame); } @@ -869,7 +950,9 @@ mod tests { async fn test_deserialize_null() { let bytes = Bytes::from_static(b"_\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize null frame: {:?}", err)); assert_eq!(frame, Frame::Null); } @@ -879,13 +962,17 @@ mod tests { async fn test_deserialize_boolean() { let bytes = Bytes::from_static(b"#t\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize boolean frame: {:?}", err)); assert_eq!(frame, Frame::Boolean(true)); let bytes = Bytes::from_static(b"#f\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize false boolean frame: {:?}", err)); assert_eq!(frame, Frame::Boolean(false)); } @@ -895,13 +982,17 @@ mod tests { async fn test_deserialize_double() { let bytes = Bytes::from_static(b",123.456\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize double frame: {:?}", err)); assert_eq!(frame, Frame::Double(123.456)); let bytes = Bytes::from_static(b",nan\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize NaN double frame: {:?}", err)); if let Frame::Double(val) = frame { assert!(val.is_nan()); @@ -911,13 +1002,20 @@ mod tests { let bytes = Bytes::from_static(b",inf\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize infinity double frame: {:?}", err)); assert_eq!(frame, Frame::Double(f64::INFINITY)); let bytes = Bytes::from_static(b",-inf\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes).await.unwrap_or_else(|err| { + panic!( + "Failed to deserialize negative infinity double frame: {:?}", + err + ) + }); assert_eq!(frame, Frame::Double(f64::NEG_INFINITY)); } @@ -927,13 +1025,17 @@ mod tests { async fn test_deserialize_bulk_error() { let bytes = Bytes::from_static(b"!11\r\nHello Redis\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize bulk error frame: {:?}", err)); assert_eq!(frame, Frame::BulkError(Bytes::from_static(b"Hello Redis"))); let bytes = Bytes::from_static(b"!0\r\n\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes).await.unwrap_or_else(|err| { + panic!("Failed to deserialize empty bulk error frame: {:?}", err) + }); assert_eq!(frame, Frame::BulkError(Bytes::from_static(b""))); } @@ -943,7 +1045,9 @@ mod tests { async fn test_deserialize_verbatim_string() { let bytes = Bytes::from_static(b"=15\r\ntxt:Some string\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize verbatim string frame: {:?}", err)); assert_eq!( frame, @@ -955,7 +1059,12 @@ mod tests { let bytes = Bytes::from_static(b"=4\r\ntxt:\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes).await.unwrap_or_else(|err| { + panic!( + "Failed to deserialize empty verbatim string frame: {:?}", + err + ) + }); assert_eq!( frame, @@ -968,7 +1077,9 @@ mod tests { async fn test_deserialize_map() { let bytes = Bytes::from_static(b"%1\r\n+key\r\n+value\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize map frame: {:?}", err)); let mut expected_frame = Frame::Map(Vec::new()); expected_frame @@ -976,7 +1087,7 @@ mod tests { Frame::SimpleString("key".to_string()), Frame::SimpleString("value".to_string()), ) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to deserialize map frame: {:?}", err)); assert_eq!(frame, expected_frame); } @@ -986,15 +1097,17 @@ mod tests { async fn test_deserialize_set() { let bytes = Bytes::from_static(b"~2\r\n$5\r\nHello\r\n$5\r\nRedis\r\n"); - let frame = Frame::deserialize(bytes).await.unwrap(); + let frame = Frame::deserialize(bytes) + .await + .unwrap_or_else(|err| panic!("Failed to deserialize set frame: {:?}", err)); let mut expected_frame = Frame::Set(Vec::new()); expected_frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Hello"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to deserialize set frame: {:?}", err)); expected_frame .push_frame_to_array(Frame::BulkString(Bytes::from_static(b"Redis"))) - .unwrap(); + .unwrap_or_else(|err| panic!("Failed to deserialize set frame: {:?}", err)); assert_eq!(frame, expected_frame); } diff --git a/tests/integration.rs b/tests/integration.rs index eb5cacc..76f1be2 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -25,7 +25,9 @@ async fn setup_redis() -> &'static testcontainers::ContainerAsync .with_wait_for(WaitFor::message_on_stdout("Ready to accept connections")) .start() .await - .unwrap() + .unwrap_or_else(|err| { + panic!("Failed to start Redis container: {:?}", err); + }) }) .await .to_owned(); @@ -43,7 +45,7 @@ async fn redis_async_cli_ping() -> TestResult { let host = container.get_host().await?; let host_port = container.get_host_port_ipv4(REDIS_PORT).await?; - let mut cmd = Command::cargo_bin("redis-async-cli").unwrap(); + let mut cmd = Command::cargo_bin("redis-async-cli")?; cmd.args([ "--host", @@ -68,7 +70,7 @@ async fn redis_async_cli_set_get() -> TestResult { let host = container.get_host().await?; let host_port = container.get_host_port_ipv4(REDIS_PORT).await?; - let mut cmd = Command::cargo_bin("redis-async-cli").unwrap(); + let mut cmd = Command::cargo_bin("redis-async-cli")?; cmd.args([ "--host", @@ -83,7 +85,7 @@ async fn redis_async_cli_set_get() -> TestResult { .stdout(predicate::str::contains("OK")) .stderr(predicate::str::is_empty()); - let mut cmd = Command::cargo_bin("redis-async-cli").unwrap(); + let mut cmd = Command::cargo_bin("redis-async-cli")?; cmd.args([ "--host", From 52f77c18851af9dfff253a5bbaa3edae681dfa86 Mon Sep 17 00:00:00 2001 From: Aden-Q Date: Fri, 4 Apr 2025 16:05:23 -0500 Subject: [PATCH 2/2] chore: do not allow unsafe code --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index c2a4475..1beb312 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,3 +50,6 @@ match_bool = "warn" needless_bool = "deny" unwrap_used = "warn" expect_used = "warn" + +[lints.rust] +unsafe_code = "forbid"