├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── benches └── basic.rs ├── docs └── commands │ ├── connection.md │ ├── keys.md │ ├── lists.md │ ├── sets.md │ └── sorted_sets.md ├── rust-toolchain ├── rustfmt.toml ├── src ├── client.rs ├── config.rs ├── connection.rs ├── error.rs ├── lib.rs ├── macros.rs ├── pipeline.rs ├── pool.rs └── protocol │ ├── maps.rs │ ├── mod.rs │ ├── numbers.rs │ ├── sequences.rs │ ├── sets.rs │ └── strings.rs └── tests ├── test_connection_commands.rs ├── test_hashes_commands.rs ├── test_keys_commands.rs ├── test_lists_commands.rs ├── test_protocol.rs ├── test_sets_commands.rs └── test_sorted_sets.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | ubuntu: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: build 13 | run: make build 14 | - name: lint 15 | run: make lint 16 | - name: unittest 17 | run: make test 18 | services: 19 | redis: 20 | image: redis 21 | ports: 22 | - 6379:6379 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | .idea 13 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "redisclient" 3 | version = "0.1.2" 4 | authors = ["liutao "] 5 | edition = "2018" 6 | keywords = ["redis", "database"] 7 | description = "Redis client for Rust" 8 | repository = "https://github.com/ltoddy/redis-rs" 9 | license = "MIT" 10 | readme = "README.md" 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 LiuTao 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build test lint bench 2 | 3 | build: 4 | cargo build --verbose 5 | 6 | test: 7 | cargo test --verbose -- --test-threads=1 8 | 9 | lint: 10 | cargo clippy --release --all --tests --verbose -- -D clippy::all -D warnings 11 | 12 | bench: 13 | cargo +nightly bench -- --test-threads=1 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | redis-rs 2 | ======== 3 | 4 | ![ci](https://github.com/ltoddy/redis-rs/workflows/ci/badge.svg) 5 | ![latest version](https://img.shields.io/crates/v/redisclient.svg) 6 | ![doc](https://docs.rs/redisclient/badge.svg) 7 | 8 | Redis client for Rust. 9 | 10 | - Pure Rust, and doesn't depend on any 3rd party libraries 11 | 12 | *Cargo.toml* 13 | 14 | ```toml 15 | [dependencies.redisclient] 16 | version = "*" 17 | ``` 18 | 19 | *src/main.rs* 20 | 21 | ```rust 22 | extern crate redisclient; 23 | 24 | use redisclient::RedisResult; 25 | use redisclient::RedisClient; 26 | 27 | fn main() { 28 | if let Err(e) = run() { 29 | println!("Error -> {}", e); 30 | } 31 | } 32 | 33 | fn run() -> RedisResult<()> { 34 | let mut client = RedisClient::new()?; 35 | client.mset(vec![("key1", 1), ("key2", 2)])?; 36 | 37 | let values: Vec = client.mget(vec!["key1", "key2"])?; 38 | println!("values -> {:?}", values); 39 | 40 | let values: Vec = client.mget(vec!["key1", "key2"])?; 41 | println!("values -> {:?}", values); 42 | 43 | Ok(()) 44 | } 45 | ``` 46 | -------------------------------------------------------------------------------- /benches/basic.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | use test::Bencher; 6 | 7 | use redisclient::RedisClient; 8 | 9 | #[bench] 10 | fn bench_strings_command(b: &mut Bencher) { 11 | let mut client = RedisClient::new().unwrap(); 12 | 13 | b.iter(|| { 14 | let key = "test_key"; 15 | client.simple_set(key, 42).unwrap(); 16 | let _: isize = client.get(key).unwrap(); 17 | client.del(vec![key]).unwrap(); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /docs/commands/connection.md: -------------------------------------------------------------------------------- 1 | ## Commection Commands 2 | 3 | see more: https://redis.io/commands#connection 4 | 5 | - [x] AUTH [username] password 6 | - [ ] CLIENT CACHING YES|NO 7 | - [ ] CLIENT GETNAME 8 | - [ ] CLIENT GETREDIR 9 | - [ ] CLIENT ID 10 | - [ ] CLIENT INFO 11 | - [ ] CLIENT KILL [ip:port] [ID client-id] [TYPE normal|master|slave|pubsub] [USER username] [ADDR ip:port] [SKIPME yes/no] 12 | - [ ] CLIENT LIST [TYPE normal|master|replica|pubsub] [ID client-id [client-id ...]] 13 | - [ ] CLIENT PAUSE timeout 14 | - [ ] CLIENT REPLY ON|OFF|SKIP 15 | - [ ] CLIENT SETNAME connection-name 16 | - [ ] CLIENT TRACKING ON|OFF [REDIRECT client-id] [PREFIX prefix [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP] 17 | - [ ] CLIENT UNBLOCK client-id [TIMEOUT|ERROR] 18 | - [x] ECHO message 19 | - [ ] HELLO protover [AUTH username password] [SETNAME clientname] 20 | - [x] PING [message] 21 | - [x] QUIT 22 | - [ ] RESET 23 | - [x] SELECT index 24 | -------------------------------------------------------------------------------- /docs/commands/keys.md: -------------------------------------------------------------------------------- 1 | ## Keys Commands 2 | 3 | see more: https://redis.io/commands#generic 4 | 5 | - [ ] COPY source destination [DB destination-db] [REPLACE] 6 | - [x] DEL DEL key [key ...] 7 | - [ ] DUMP key 8 | - [ ] EXISTS key [key ...] 9 | - [x] EXPIRE key seconds 10 | - [ ] EXPIREAT key timestamp 11 | - [x] KEYS pattern 12 | - [ ] MIGRATE host port key|"" destination-db timeout [COPY] [REPLACE] [AUTH password] [AUTH2 username password] [KEYS key [key ...]] 13 | - [ ] MOVE key db 14 | - [ ] OBJECT subcommand [arguments [arguments ...]] 15 | - [x] PERSIST key 16 | - [x] PEXPIRE key milliseconds 17 | - [ ] PEXPIREAT key milliseconds-timestamp 18 | - [x] PTTL key 19 | - [x] RANDOMKEY 20 | - [x] RENAME key newkey 21 | - [x] RENAMENX key newkey 22 | - [ ] RESTORE key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency] 23 | - [ ] SCAN cursor [MATCH pattern] [COUNT count] [TYPE type] 24 | - [ ] SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination] 25 | - [ ] TOUCH key [key ...] 26 | - [x] TTL key 27 | - [x] TYPE ke 28 | - [x] UNLINK key [key ...] 29 | - [ ] WAIT numreplicas timeout 30 | -------------------------------------------------------------------------------- /docs/commands/lists.md: -------------------------------------------------------------------------------- 1 | ## Lists Commands 2 | 3 | see more: https://redis.io/commands#list 4 | 5 | - [ ] BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout 6 | - [ ] BLPOP key [key ...] timeout 7 | - [ ] BRPOP key [key ...] timeout 8 | - [x] BRPOPLPUSH source destination timeout 9 | - [x] LINDEX key index 10 | - [x] LINSERT key BEFORE|AFTER pivot element 11 | - [x] LLEN key 12 | - [ ] LMOVE source destination LEFT|RIGHT LEFT|RIGHT 13 | - [x] LPOP key 14 | - [ ] LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len] 15 | - [x] LPUSH key element [element ...] 16 | - [x] LPUSHX key element [element ...] 17 | - [x] LRANGE key start stop 18 | - [x] LREM key count element 19 | - [x] LSET key index element 20 | - [x] LTRIM key start stop 21 | - [x] RPOP key 22 | - [x] RPOPLPUSH source destination 23 | - [x] RPUSH key element [element ...] 24 | - [x] RPUSHX key element [element ...] 25 | -------------------------------------------------------------------------------- /docs/commands/sets.md: -------------------------------------------------------------------------------- 1 | ## Sets Commands 2 | 3 | see more: https://redis.io/commands#set 4 | 5 | - [x] SADD key member [member ...] 6 | - [x] SCARD key 7 | - [x] SDIFF key [key ...] 8 | - [x] SDIFFSTORE destination key [key ...] 9 | - [x] SINTER key [key ...] 10 | - [x] SINTERSTORE destination key [key ...] 11 | - [x] SISMEMBER key member 12 | - [x] SMEMBERS key 13 | - [x] SMISMEMBER key member [member ...] 14 | - [x] SMOVE source destination member 15 | - [x] SPOP key [count] 16 | - [x] SRANDMEMBER key [count] 17 | - [x] SREM key member [member ...] 18 | - [ ] SSCAN key cursor [MATCH pattern] [COUNT count] 19 | - [x] SUNION key [key ...] 20 | - [x] SUNIONSTORE destination key [key ...] 21 | -------------------------------------------------------------------------------- /docs/commands/sorted_sets.md: -------------------------------------------------------------------------------- 1 | ## Sorted-Sets Commands 2 | 3 | see more: https://redis.io/commands#sorted-sets 4 | 5 | - [ ] BZPOPMAX key [key ...] timeout 6 | - [ ] BZPOPMIN key [key ...] timeout 7 | - [ ] ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...] 8 | - [x] ZCARD key 9 | - [x] ZCOUNT key min max 10 | - [ ] ZDIFF numkeys key [key ...] [WITHSCORES] 11 | - [ ] ZDIFFSTORE destination numkeys key [key ...] 12 | - [ ] ZINCRBY key increment member 13 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | stable 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 120 2 | -------------------------------------------------------------------------------- /src/client.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::hash::Hash; 3 | 4 | use crate::config::RedisConfig; 5 | use crate::connection::Reply; 6 | use crate::error::{ErrorKind, RedisError}; 7 | use crate::pipeline::Pipeline; 8 | use crate::pool::ConnectionPool; 9 | use crate::protocol::{RedisDeserializationProtocol, RedisSerializationProtocol}; 10 | use crate::{DataType, RedisResult}; 11 | 12 | pub struct Command { 13 | cmd: String, 14 | args: Vec, 15 | count: usize, 16 | } 17 | 18 | impl Command { 19 | fn new(cmd: S) -> Command { 20 | let cmd = cmd.to_string(); 21 | let args = Vec::new(); 22 | Command { cmd, args, count: 1 } 23 | } 24 | 25 | fn arg(&mut self, arg: T) -> &mut Self { 26 | self.args.extend(arg.serialization()); 27 | self.count += 1; 28 | self 29 | } 30 | 31 | fn into_vec(self) -> Vec { 32 | let Command { cmd, args, count } = self; 33 | 34 | let mut buf = Vec::new(); 35 | buf.extend(Vec::from(format!("*{}\r\n", count))); 36 | buf.extend(cmd.serialization()); 37 | buf.extend(args); 38 | buf 39 | } 40 | } 41 | 42 | macro_rules! command { 43 | ($name: expr; args => $($args: expr),*) => { 44 | { 45 | let mut cmd = Command::new($name); 46 | $(cmd.arg($args);)* 47 | cmd 48 | } 49 | }; 50 | } 51 | 52 | // TODO: remove 53 | pub enum ListBeforeOrAfter { 54 | Before, 55 | After, 56 | } 57 | 58 | pub struct RedisClient { 59 | pool: ConnectionPool, 60 | } 61 | 62 | impl RedisClient { 63 | pub fn new() -> RedisResult { 64 | let config = RedisConfig::default(); 65 | 66 | Self::with_config(config) 67 | } 68 | 69 | pub fn with_config(config: RedisConfig) -> RedisResult { 70 | let RedisConfig { 71 | address, 72 | database, 73 | username, 74 | password, 75 | pool_capacity, 76 | } = config; 77 | 78 | let mut client = RedisClient { 79 | pool: ConnectionPool::new(pool_capacity, address), 80 | }; 81 | 82 | if let Some(password) = password { 83 | client.auth(username, password)?; 84 | } 85 | 86 | if database > 0 { 87 | client.select(database)?; 88 | } 89 | 90 | Ok(client) 91 | } 92 | 93 | pub fn pipe(&self) -> Pipeline { 94 | Pipeline::new(self) 95 | } 96 | 97 | pub fn pipe_with_capacity(&self, capacity: usize) -> Pipeline { 98 | Pipeline::with_capacity(self, capacity) 99 | } 100 | 101 | // TODO 102 | pub fn flushall(&mut self) -> RedisResult<()> { 103 | let cmd = Command::new("FLUSHALL"); 104 | 105 | let reply = self.execute(cmd)?; 106 | <()>::deserialization(reply) 107 | } 108 | 109 | // Connection commands 110 | /// The AUTH command authenticates the current connection 111 | /// 112 | /// Return value: Simple string reply 113 | pub fn auth(&mut self, username: Option, password: S) -> RedisResult<()> 114 | where 115 | S: ToString, 116 | { 117 | let mut cmd = Command::new("AUTH"); 118 | if let Some(username) = username { 119 | cmd.arg(username.to_string()); 120 | } 121 | cmd.arg(password.to_string()); 122 | let reply = self.execute(cmd)?; 123 | <()>::deserialization(reply) 124 | } 125 | 126 | /// Returns message. 127 | /// 128 | /// Return value: Bulk string reply 129 | pub fn echo(&mut self, message: S) -> RedisResult 130 | where 131 | S: ToString, 132 | { 133 | let cmd = command!("ECHO"; args => message.to_string()); 134 | let reply = self.execute(cmd)?; 135 | ::deserialization(reply) 136 | } 137 | 138 | /// Returns PONG if no argument is provided, otherwise return a copy of the argument as a bulk. 139 | /// 140 | /// Return value: Simple string reply 141 | pub fn ping(&mut self) -> RedisResult<()> { 142 | let cmd = Command::new("PING"); 143 | let reply = self.execute(cmd)?; 144 | <()>::deserialization(reply) 145 | } 146 | 147 | /// Ask the server to close the connection. 148 | /// 149 | /// Return value: Simple string reply 150 | pub fn quit(&mut self) -> RedisResult<()> { 151 | let cmd = Command::new("QUIT"); 152 | let reply = self.execute(cmd)?; 153 | <()>::deserialization(reply) 154 | } 155 | 156 | /// Select the Redis logical database having the specified zero-based numeric index. 157 | /// 158 | /// Return value: Simple string reply 159 | pub fn select(&mut self, index: u8) -> RedisResult<()> { 160 | let cmd = command!("SELECT"; args => index); 161 | let reply = self.execute(cmd)?; 162 | <()>::deserialization(reply) 163 | } 164 | 165 | // Hashes commands 166 | /// Removes the specified fields from the hash stored at key. 167 | /// 168 | /// Return value: Integer reply 169 | pub fn hdel(&mut self, key: K, fields: Vec) -> RedisResult 170 | where 171 | K: RedisSerializationProtocol, 172 | { 173 | let mut cmd = command!("HDEL"; args => key); 174 | for field in fields { 175 | cmd.arg(field); 176 | } 177 | let reply = self.execute(cmd)?; 178 | ::deserialization(reply) 179 | } 180 | 181 | /// Returns if field is an existing field in the hash stored at key. 182 | /// 183 | /// Return value: Integer reply 184 | pub fn hexists(&mut self, key: K, field: F) -> RedisResult 185 | where 186 | K: RedisSerializationProtocol, 187 | F: RedisSerializationProtocol, 188 | { 189 | let cmd = command!("HEXISTS"; args => key, field); 190 | 191 | let reply = self.execute(cmd)?; 192 | ::deserialization(reply) 193 | } 194 | 195 | /// Returns the value associated with field in the hash stored at key. 196 | /// 197 | /// Return value: Bulk string reply 198 | pub fn hget(&mut self, key: K, field: F) -> RedisResult 199 | where 200 | K: RedisSerializationProtocol, 201 | F: RedisSerializationProtocol, 202 | V: RedisDeserializationProtocol, 203 | { 204 | let cmd = command!("HGET"; args => key, field); 205 | let reply = self.execute(cmd)?; 206 | ::deserialization(reply) 207 | } 208 | 209 | /// Returns all fields and values of the hash stored at key. 210 | /// 211 | /// Return value: Array reply 212 | pub fn hgetall(&mut self, key: K) -> RedisResult 213 | where 214 | K: RedisSerializationProtocol, 215 | M: RedisDeserializationProtocol, 216 | { 217 | let cmd = command!("HGETALL"; args => key); 218 | let reply = self.execute(cmd)?; 219 | ::deserialization(reply) 220 | } 221 | 222 | /// Increments the number stored at field in the hash stored at key by increment. 223 | /// 224 | /// Return value: Integer value 225 | pub fn hincrby(&mut self, key: K, field: F, increment: i64) -> RedisResult 226 | where 227 | K: RedisSerializationProtocol, 228 | F: RedisSerializationProtocol, 229 | { 230 | let cmd = command!("HINCRBY"; args => key, field, increment); 231 | let reply = self.execute(cmd)?; 232 | ::deserialization(reply) 233 | } 234 | 235 | /// Increment the specified field of a hash stored at key, and representing a floating point number, by the specified increment. 236 | /// 237 | /// Return value: Bulk string reply 238 | pub fn hincrbyfloat(&mut self, key: K, field: F, increment: f64) -> RedisResult 239 | where 240 | K: RedisSerializationProtocol, 241 | F: RedisSerializationProtocol, 242 | { 243 | let cmd = command!("HINCRBYFLOAT"; args => key, field, increment); 244 | let reply = self.execute(cmd)?; 245 | ::deserialization(reply) 246 | } 247 | 248 | /// Returns all field names in the hash stored at key. 249 | /// 250 | /// Return value: Array reply 251 | pub fn hkeys(&mut self, key: K) -> RedisResult> 252 | where 253 | K: RedisSerializationProtocol, 254 | V: RedisDeserializationProtocol, 255 | { 256 | let cmd = command!("HKEYS"; args => key); 257 | let reply = self.execute(cmd)?; 258 | >::deserialization(reply) 259 | } 260 | 261 | /// Returns the number of fields contained in the hash stored at key. 262 | /// 263 | /// Return value: Integer reply 264 | pub fn hlen(&mut self, key: K) -> RedisResult 265 | where 266 | K: RedisSerializationProtocol, 267 | { 268 | let cmd = command!("HLEN"; args => key); 269 | let reply = self.execute(cmd)?; 270 | ::deserialization(reply) 271 | } 272 | 273 | /// Returns the values associated with the specified fields in the hash stored at key. 274 | /// 275 | /// Return value: Array reply 276 | pub fn hmget(&mut self, key: K, fields: Vec) -> RedisResult> 277 | where 278 | K: RedisSerializationProtocol, 279 | F: RedisSerializationProtocol, 280 | V: RedisDeserializationProtocol, 281 | { 282 | let mut cmd = command!("HMGET"; args => key); 283 | for field in fields { 284 | cmd.arg(field); 285 | } 286 | let reply = self.execute(cmd)?; 287 | >::deserialization(reply) 288 | } 289 | 290 | /// Sets the specified fields to their respective values in the hash stored at key. 291 | /// 292 | /// Return values: Simple string reply 293 | pub fn hmset(&mut self, key: K, fvs: Vec<(F, V)>) -> RedisResult<()> 294 | where 295 | K: RedisSerializationProtocol, 296 | F: RedisSerializationProtocol, 297 | V: RedisSerializationProtocol, 298 | { 299 | let mut cmd = command!("HMSET"; args => key); 300 | for (field, value) in fvs { 301 | cmd.arg(field).arg(value); 302 | } 303 | let reply = self.execute(cmd)?; 304 | <()>::deserialization(reply) 305 | } 306 | 307 | pub fn hscan(&mut self) { 308 | todo!(); 309 | } 310 | 311 | /// Sets field in the hash stored at key to value. 312 | /// 313 | /// Return value: Integer reply 314 | pub fn hset(&mut self, key: K, field: F, value: V) -> RedisResult 315 | where 316 | K: RedisSerializationProtocol, 317 | F: RedisSerializationProtocol, 318 | V: RedisSerializationProtocol, 319 | { 320 | let cmd = command!("HSET"; args => key, field, value); 321 | let reply = self.execute(cmd)?; 322 | ::deserialization(reply) 323 | } 324 | 325 | /// Sets field in the hash stored at key to value, only if field does not yet exist. 326 | /// 327 | /// Return value: Integer value 328 | pub fn hsetnx(&mut self, key: K, field: F, value: V) -> RedisResult 329 | where 330 | K: RedisSerializationProtocol, 331 | F: RedisSerializationProtocol, 332 | V: RedisSerializationProtocol, 333 | { 334 | let cmd = command!("HSETNX"; args => key, field, value); 335 | let reply = self.execute(cmd)?; 336 | ::deserialization(reply) 337 | } 338 | 339 | /// Returns the string length of the value associated with field in the hash stored at key. 340 | /// 341 | /// Return value: Integer reply 342 | pub fn hstrlen(&mut self, key: K, field: F) -> RedisResult 343 | where 344 | K: RedisSerializationProtocol, 345 | F: RedisSerializationProtocol, 346 | { 347 | let cmd = command!("HSTRLEN"; args => key, field); 348 | let reply = self.execute(cmd)?; 349 | ::deserialization(reply) 350 | } 351 | 352 | /// Returns all values in the hash stored at key. 353 | /// 354 | /// Return value: Array reply 355 | pub fn hvals(&mut self, key: K) -> RedisResult> 356 | where 357 | K: RedisSerializationProtocol, 358 | V: RedisDeserializationProtocol, 359 | { 360 | let cmd = command!("HVALS"; args => key); 361 | let reply = self.execute(cmd)?; 362 | >::deserialization(reply) 363 | } 364 | 365 | // keys command 366 | /// This command copies the value stored at the source key to the destination key. 367 | /// 368 | /// Return value: Integer reply 369 | pub fn copy(&mut self) -> RedisResult<()> { 370 | todo!() 371 | } 372 | 373 | /// Removes the specified keys. A key is ignored if it does not exist. 374 | /// 375 | /// Return value: Integer reply 376 | pub fn del(&mut self, keys: Vec) -> RedisResult 377 | where 378 | K: RedisSerializationProtocol, 379 | { 380 | let mut cmd = Command::new("DEL"); 381 | for key in keys { 382 | cmd.arg(key); 383 | } 384 | 385 | let reply = self.execute(cmd)?; 386 | ::deserialization(reply) 387 | } 388 | 389 | /// Serialize the value stored at key in a Redis-specific format and return it to the user. 390 | /// 391 | /// Return value: Bulk string reply 392 | #[allow(unused_variables)] 393 | pub fn dump(&mut self, key: K) -> RedisResult 394 | where 395 | K: RedisSerializationProtocol, 396 | { 397 | // let mut cmd = Command::new("DUMP"); 398 | // cmd.arg(key); 399 | // 400 | // let reply = self.execute(cmd)?; 401 | // ::deserialization(reply) 402 | todo!() 403 | } 404 | 405 | /// Returns if key exists. 406 | /// 407 | /// Return value: Integer reply 408 | pub fn exists(&mut self, keys: Vec) -> RedisResult 409 | where 410 | K: RedisSerializationProtocol, 411 | { 412 | let mut cmd = Command::new("EXISTS"); 413 | for key in keys { 414 | cmd.arg(key); 415 | } 416 | 417 | let reply = self.execute(cmd)?; 418 | ::deserialization(reply) 419 | } 420 | 421 | /// Set a timeout on key. After the timeout has expired, the key will automatically be deleted. 422 | /// 423 | /// Return value: Integer reply 424 | pub fn expire(&mut self, key: K, seconds: usize) -> RedisResult 425 | where 426 | K: RedisSerializationProtocol, 427 | { 428 | let cmd = command!("EXPIRE"; args => key, seconds); 429 | let reply = self.execute(cmd)?; 430 | ::deserialization(reply) 431 | } 432 | 433 | /// EXPIREAT has the same effect and semantic as EXPIRE, but instead of specifying the number of seconds representing the TTL, it takes an absolute Unix timestamp. 434 | /// 435 | /// Return value: Integer reply 436 | #[allow(unused_variables)] 437 | pub fn expireat(&mut self, key: K, timestamp: u64) -> RedisResult 438 | where 439 | K: RedisSerializationProtocol, 440 | { 441 | todo!() 442 | } 443 | 444 | /// Returns all keys matching pattern. 445 | /// 446 | /// Return value: Array reply 447 | pub fn keys(&mut self, pattern: S) -> RedisResult> 448 | where 449 | S: ToString, 450 | { 451 | let cmd = command!("KEYS"; args => pattern.to_string()); 452 | let reply = self.execute(cmd)?; 453 | >::deserialization(reply) 454 | } 455 | 456 | /// Remove the existing timeout on key, turning the key from volatile to persistent. 457 | /// 458 | /// Return value: Integer reply 459 | pub fn persist(&mut self, key: K) -> RedisResult 460 | where 461 | K: RedisSerializationProtocol, 462 | { 463 | let cmd = command!("PERSIST"; args => key); 464 | let reply = self.execute(cmd)?; 465 | ::deserialization(reply) 466 | } 467 | 468 | /// This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds instead of seconds. 469 | /// 470 | /// Return value: Integer reply 471 | pub fn pexpire(&mut self, key: K, milliseconds: u64) -> RedisResult 472 | where 473 | K: RedisSerializationProtocol, 474 | { 475 | let cmd = command!("PEXPIRE"; args => key, milliseconds); 476 | let reply = self.execute(cmd)?; 477 | ::deserialization(reply) 478 | } 479 | 480 | /// Like TTL this command returns the remaining time to live of a key that has an expire set, 481 | /// with the sole difference that TTL returns the amount of remaining time in seconds while PTTL returns it in milliseconds. 482 | /// 483 | /// Return value: Integer reply 484 | pub fn pttl(&mut self, key: K) -> RedisResult 485 | where 486 | K: RedisSerializationProtocol, 487 | { 488 | let cmd = command!("PTTL"; args => key); 489 | let reply = self.execute(cmd)?; 490 | ::deserialization(reply) 491 | } 492 | 493 | /// Return a random key from the currently selected database. 494 | /// 495 | /// Return value: Bulk string reply 496 | pub fn randomkey(&mut self) -> RedisResult { 497 | let cmd = Command::new("RANDOMKEY"); 498 | let reply = self.execute(cmd)?; 499 | ::deserialization(reply) 500 | } 501 | 502 | /// Renames key to newkey. It returns an error when key does not exist. 503 | /// 504 | /// Return value: Simple string reply 505 | pub fn rename(&mut self, key: K, newkey: K) -> RedisResult<()> 506 | where 507 | K: RedisSerializationProtocol, 508 | { 509 | let cmd = command!("RENAME"; args => key, newkey); 510 | let reply = self.execute(cmd)?; 511 | <()>::deserialization(reply) 512 | } 513 | 514 | /// Renames key to newkey if newkey does not yet exist. It returns an error when key does not exist. 515 | /// 516 | /// Return value: Integer reply 517 | pub fn renamenx(&mut self, key: K, newkey: K) -> RedisResult 518 | where 519 | K: RedisSerializationProtocol, 520 | { 521 | let cmd = command!("RENAMENX"; args => key, newkey); 522 | let reply = self.execute(cmd)?; 523 | ::deserialization(reply) 524 | } 525 | 526 | /// Alters the last access time of a key(s). A key is ignored if it does not exist. 527 | /// 528 | /// Return value: Integer reply 529 | pub fn touch(&mut self, keys: Vec) -> RedisResult 530 | where 531 | K: RedisSerializationProtocol, 532 | { 533 | let mut cmd = Command::new("TOUCH"); 534 | for key in keys { 535 | cmd.arg(key); 536 | } 537 | let reply = self.execute(cmd)?; 538 | ::deserialization(reply) 539 | } 540 | 541 | /// Returns the remaining time to live of a key that has a timeout. 542 | /// 543 | /// Return value: Integer reply 544 | pub fn ttl(&mut self, key: K) -> RedisResult 545 | where 546 | K: RedisSerializationProtocol, 547 | { 548 | let cmd = command!("TTL"; args => key); 549 | let reply = self.execute(cmd)?; 550 | ::deserialization(reply) 551 | } 552 | 553 | /// Returns the string representation of the type of the value stored at key. 554 | /// 555 | /// Return value: Simple string reply 556 | pub fn type_(&mut self, key: K) -> RedisResult 557 | where 558 | K: RedisSerializationProtocol, 559 | { 560 | let cmd = command!("TYPE"; args => key); 561 | let reply = self.execute(cmd)?; 562 | ::deserialization(reply) 563 | } 564 | 565 | /// This command is very similar to DEL: it removes the specified keys. 566 | /// 567 | /// Return value: Integer reply 568 | pub fn unlink(&mut self, keys: Vec) -> RedisResult 569 | where 570 | K: RedisSerializationProtocol, 571 | { 572 | let mut cmd = Command::new("UNLINK"); 573 | for key in keys { 574 | cmd.arg(key); 575 | } 576 | let reply = self.execute(cmd)?; 577 | ::deserialization(reply) 578 | } 579 | 580 | /// BRPOPLPUSH is the blocking variant of RPOPLPUSH. 581 | /// 582 | /// Return value: Bulk string reply 583 | pub fn brpoplpush(&mut self, source: K, destination: K, timeout: usize) -> RedisResult 584 | where 585 | K: RedisSerializationProtocol, 586 | E: RedisDeserializationProtocol, 587 | { 588 | let cmd = command!("BRPOPLPUSH"; args => source, destination, timeout); 589 | let reply = self.execute(cmd)?; 590 | ::deserialization(reply) 591 | } 592 | 593 | // Lists commands 594 | /// Returns the element at index index in the list stored at key. 595 | /// 596 | /// Return value: Bulk string reply 597 | pub fn lindex(&mut self, key: K, index: isize) -> RedisResult 598 | where 599 | K: RedisSerializationProtocol, 600 | V: RedisDeserializationProtocol, 601 | { 602 | let cmd = command!("LINDEX"; args => key, index); 603 | let reply = self.execute(cmd)?; 604 | ::deserialization(reply) 605 | } 606 | 607 | /// Inserts element in the list stored at key either before or after the reference value pivot. 608 | /// 609 | /// Return value: Integer reply 610 | pub fn linsert(&mut self, key: K, operator: ListBeforeOrAfter, pivot: E, element: E) -> RedisResult 611 | where 612 | K: RedisSerializationProtocol, 613 | E: RedisSerializationProtocol, 614 | { 615 | let cmd = command!("LINSERT"; args => key, operator, pivot, element); 616 | let reply = self.execute(cmd)?; 617 | ::deserialization(reply) 618 | } 619 | 620 | /// Returns the length of the list stored at key. 621 | /// 622 | /// Return value: Integer reply 623 | pub fn llen(&mut self, key: K) -> RedisResult 624 | where 625 | K: RedisSerializationProtocol, 626 | { 627 | let cmd = command!("LLEN"; args => key); 628 | let reply = self.execute(cmd)?; 629 | ::deserialization(reply) 630 | } 631 | 632 | /// Removes and returns the first element of the list stored at key. 633 | /// 634 | /// Return value: Bulk string reply 635 | pub fn lpop(&mut self, key: K) -> RedisResult 636 | where 637 | K: RedisSerializationProtocol, 638 | E: RedisDeserializationProtocol, 639 | { 640 | let cmd = command!("LPOP"; args => key); 641 | let reply = self.execute(cmd)?; 642 | ::deserialization(reply) 643 | } 644 | 645 | /// The command returns the index of matching elements inside a Redis list. 646 | pub fn lpos(&mut self) { 647 | todo!() 648 | } 649 | 650 | /// Insert all the specified values at the head of the list stored at key. 651 | /// 652 | /// Retrun value: Integer reply 653 | pub fn lpush(&mut self, key: K, elements: Vec) -> RedisResult 654 | where 655 | K: RedisSerializationProtocol, 656 | E: RedisSerializationProtocol, 657 | { 658 | let mut cmd = command!("LPUSH"; args => key); 659 | for element in elements { 660 | cmd.arg(element); 661 | } 662 | let reply = self.execute(cmd)?; 663 | ::deserialization(reply) 664 | } 665 | 666 | /// Inserts specified values at the head of the list stored at key, only if key already exists and holds a list. 667 | /// 668 | /// Return value: Integer value 669 | pub fn lpushx(&mut self, key: K, elements: Vec) -> RedisResult 670 | where 671 | K: RedisSerializationProtocol, 672 | E: RedisSerializationProtocol, 673 | { 674 | let mut cmd = command!("LPUSHX"; args => key); 675 | for element in elements { 676 | cmd.arg(element); 677 | } 678 | let reply = self.execute(cmd)?; 679 | ::deserialization(reply) 680 | } 681 | 682 | /// Returns the specified elements of the list stored at key. 683 | /// 684 | /// Return value: Array reply 685 | pub fn lrange(&mut self, key: K, start: isize, end: isize) -> RedisResult> 686 | where 687 | K: RedisSerializationProtocol, 688 | E: RedisDeserializationProtocol, 689 | { 690 | let cmd = command!("LRANGE"; args => key, start, end); 691 | let reply = self.execute(cmd)?; 692 | >::deserialization(reply) 693 | } 694 | 695 | /// Removes the first count occurrences of elements equal to element from the list stored at key. 696 | /// 697 | /// Return value: Integer reply 698 | pub fn lrem(&mut self, key: K, count: isize, element: E) -> RedisResult 699 | where 700 | K: RedisSerializationProtocol, 701 | E: RedisSerializationProtocol, 702 | { 703 | let cmd = command!("LREM"; args => key, count, element); 704 | let reply = self.execute(cmd)?; 705 | ::deserialization(reply) 706 | } 707 | 708 | /// Sets the list element at index to element. 709 | /// 710 | /// Return value: Simple string reply 711 | pub fn lset(&mut self, key: K, index: isize, element: E) -> RedisResult<()> 712 | where 713 | K: RedisSerializationProtocol, 714 | E: RedisSerializationProtocol, 715 | { 716 | let cmd = command!("LSET"; args => key, index, element); 717 | let reply = self.execute(cmd)?; 718 | <()>::deserialization(reply) 719 | } 720 | 721 | /// Trim an existing list so that it will contain only the specified range of elements specified. 722 | /// 723 | /// Return value: Simple string reply 724 | pub fn ltrim(&mut self, key: K, start: isize, stop: isize) -> RedisResult<()> 725 | where 726 | K: RedisSerializationProtocol, 727 | { 728 | let cmd = command!("LTRIM"; args => key, start, stop); 729 | let reply = self.execute(cmd)?; 730 | <()>::deserialization(reply) 731 | } 732 | 733 | /// Removes and returns the last element of the list stored at key. 734 | /// 735 | /// Return value: Bulk string reply 736 | pub fn rpop(&mut self, key: K) -> RedisResult 737 | where 738 | K: RedisSerializationProtocol, 739 | E: RedisDeserializationProtocol, 740 | { 741 | let cmd = command!("RPOP"; args => key); 742 | let reply = self.execute(cmd)?; 743 | ::deserialization(reply) 744 | } 745 | 746 | /// Atomically returns and removes the last element (tail) of the list stored at source, and 747 | /// pushes the element at the first element (head) of the list stored at destination. 748 | /// 749 | /// Return value: Bulk string reply 750 | pub fn rpoplpush(&mut self, source: K, destination: K) -> RedisResult 751 | where 752 | K: RedisSerializationProtocol, 753 | E: RedisDeserializationProtocol, 754 | { 755 | let cmd = command!("RPOPLPUSH"; args => source, destination); 756 | let reply = self.execute(cmd)?; 757 | ::deserialization(reply) 758 | } 759 | 760 | /// Insert all the specified values at the tail of the list stored at key. 761 | /// 762 | /// Return value: Integer value 763 | pub fn rpush(&mut self, key: K, elements: Vec) -> RedisResult 764 | where 765 | K: RedisSerializationProtocol, 766 | E: RedisSerializationProtocol, 767 | { 768 | let mut cmd = command!("RPUSH"; args => key); 769 | for element in elements { 770 | cmd.arg(element); 771 | } 772 | let reply = self.execute(cmd)?; 773 | ::deserialization(reply) 774 | } 775 | 776 | /// Inserts specified values at the tail of the list stored at key, only if key already exists and holds a list. 777 | /// 778 | /// Return value: Integer reply 779 | pub fn rpushx(&mut self, key: K, elements: Vec) -> RedisResult 780 | where 781 | K: RedisSerializationProtocol, 782 | E: RedisSerializationProtocol, 783 | { 784 | let mut cmd = command!("RPUSHX"; args => key); 785 | for element in elements { 786 | cmd.arg(element); 787 | } 788 | let reply = self.execute(cmd)?; 789 | ::deserialization(reply) 790 | } 791 | 792 | // Sets commands 793 | /// Add the specified members to the set stored at key. 794 | /// 795 | /// Return value: Integer value 796 | pub fn sadd(&mut self, key: K, members: HashSet) -> RedisResult 797 | where 798 | K: RedisSerializationProtocol, 799 | M: RedisSerializationProtocol + Hash + Eq, 800 | { 801 | let mut cmd = command!("SADD"; args => key); 802 | for member in members { 803 | cmd.arg(member); 804 | } 805 | let reply = self.execute(cmd)?; 806 | ::deserialization(reply) 807 | } 808 | 809 | /// Returns the set cardinality (number of elements) of the set stored at key. 810 | /// 811 | /// Return value: Integer reply 812 | pub fn scard(&mut self, key: K) -> RedisResult 813 | where 814 | K: RedisSerializationProtocol, 815 | { 816 | let cmd = command!("SCARD"; args => key); 817 | let reply = self.execute(cmd)?; 818 | ::deserialization(reply) 819 | } 820 | 821 | /// Returns the members of the set resulting from the difference between the first set and all the successive sets. 822 | /// 823 | /// Return value: Array reply 824 | pub fn sdiff(&mut self, keys: Vec) -> RedisResult> 825 | where 826 | K: RedisSerializationProtocol, 827 | M: RedisDeserializationProtocol + Hash + Eq, 828 | { 829 | let mut cmd = Command::new("SDIFF"); 830 | for key in keys { 831 | cmd.arg(key); 832 | } 833 | let reply = self.execute(cmd)?; 834 | >::deserialization(reply) 835 | } 836 | 837 | /// This command is equal to SDIFF, but instead of returning the resulting set, it is stored in destination. 838 | /// 839 | /// Return value: Integer reply 840 | pub fn sdiffstore(&mut self, destination: K, keys: Vec) -> RedisResult 841 | where 842 | K: RedisSerializationProtocol + Hash + Eq, 843 | { 844 | let mut cmd = command!("SDIFFSTORE"; args => destination); 845 | for key in keys { 846 | cmd.arg(key); 847 | } 848 | let reply = self.execute(cmd)?; 849 | ::deserialization(reply) 850 | } 851 | 852 | /// Returns the members of the set resulting from the intersection of all the given sets. 853 | /// 854 | /// Return value: Array reply 855 | pub fn sinter(&mut self, keys: Vec) -> RedisResult> 856 | where 857 | K: RedisSerializationProtocol, 858 | M: RedisDeserializationProtocol + Hash + Eq, 859 | { 860 | let mut cmd = Command::new("SINTER"); 861 | for key in keys { 862 | cmd.arg(key); 863 | } 864 | let reply = self.execute(cmd)?; 865 | >::deserialization(reply) 866 | } 867 | 868 | /// This command is equal to SINTER, but instead of returning the resulting set, it is stored in destination. 869 | /// 870 | /// Return value: Integer reply 871 | pub fn sinterstore(&mut self, destination: K, keys: Vec) -> RedisResult 872 | where 873 | K: RedisSerializationProtocol, 874 | { 875 | let mut cmd = command!("SINTERSTORE"; args => destination); 876 | for key in keys { 877 | cmd.arg(key); 878 | } 879 | let reply = self.execute(cmd)?; 880 | ::deserialization(reply) 881 | } 882 | 883 | /// Returns if member is a member of the set stored at key. 884 | /// 885 | /// Return value: Integer reply 886 | pub fn sismember(&mut self, key: K, member: M) -> RedisResult 887 | where 888 | K: RedisSerializationProtocol, 889 | M: RedisSerializationProtocol + Hash + Eq, 890 | { 891 | let cmd = command!("SISMEMBER"; args => key, member); 892 | let reply = self.execute(cmd)?; 893 | ::deserialization(reply) 894 | } 895 | 896 | /// Returns all the members of the set value stored at key. 897 | /// 898 | /// Return value: Array reply 899 | pub fn smembers(&mut self, key: K) -> RedisResult> 900 | where 901 | K: RedisSerializationProtocol, 902 | M: RedisDeserializationProtocol + Hash + Eq, 903 | { 904 | let cmd = command!("SMEMBERS"; args => key); 905 | let reply = self.execute(cmd)?; 906 | >::deserialization(reply) 907 | } 908 | 909 | /// Returns whether each member is a member of the set stored at key. 910 | /// 911 | /// Return value: Array reply 912 | pub fn smismember(&mut self, key: K, members: HashSet) -> RedisResult> 913 | where 914 | K: RedisSerializationProtocol, 915 | M: RedisSerializationProtocol + Hash + Eq, 916 | { 917 | let mut cmd = command!("SMISMEMBER"; args => key); 918 | for member in members { 919 | cmd.arg(member); 920 | } 921 | let reply = self.execute(cmd)?; 922 | >::deserialization(reply) 923 | } 924 | 925 | /// Move member from the set at source to the set at destination. 926 | /// 927 | /// Return value: Integer reply 928 | pub fn smove(&mut self, source: K, destination: K, member: M) -> RedisResult 929 | where 930 | K: RedisSerializationProtocol, 931 | M: RedisSerializationProtocol + Hash + Eq, 932 | { 933 | let cmd = command!("SMOVE"; args => source, destination, member); 934 | let reply = self.execute(cmd)?; 935 | ::deserialization(reply) 936 | } 937 | 938 | /// Removes and returns one or more random members from the set value store at key. 939 | /// 940 | /// Return value: Bulk string reply or Array reply 941 | pub fn spop(&mut self, key: K, count: Option) -> RedisResult 942 | where 943 | K: RedisSerializationProtocol, 944 | M: RedisDeserializationProtocol, 945 | { 946 | let mut cmd = command!("SPOP"; args => key); 947 | if let Some(count) = count { 948 | cmd.arg(count); 949 | } 950 | let reply = self.execute(cmd)?; 951 | ::deserialization(reply) 952 | } 953 | 954 | /// When called with just the key argument, return a random element from the set value stored at key. 955 | /// 956 | /// Return value: Bulk string reply or Array reply 957 | pub fn srandmember(&mut self, key: K, count: Option) -> RedisResult 958 | where 959 | K: RedisSerializationProtocol, 960 | M: RedisDeserializationProtocol, 961 | { 962 | let mut cmd = command!("SPOP"; args => key); 963 | if let Some(count) = count { 964 | cmd.arg(count); 965 | } 966 | let reply = self.execute(cmd)?; 967 | ::deserialization(reply) 968 | } 969 | 970 | /// Remove the specified members from the set stored at key. 971 | /// 972 | /// Return value: Integer reply 973 | pub fn srem(&mut self, key: K, members: HashSet) -> RedisResult 974 | where 975 | K: RedisSerializationProtocol, 976 | M: RedisSerializationProtocol + Hash + Eq, 977 | { 978 | let mut cmd = command!("SREM"; args => key); 979 | for member in members { 980 | cmd.arg(member); 981 | } 982 | let reply = self.execute(cmd)?; 983 | ::deserialization(reply) 984 | } 985 | 986 | /// Like Scan command 987 | pub fn sscan() { 988 | todo!() 989 | } 990 | 991 | /// Returns the members of the set resulting from the union of all the given sets. 992 | /// 993 | /// Return value: Array reply 994 | pub fn sunion(&mut self, keys: Vec) -> RedisResult> 995 | where 996 | K: RedisSerializationProtocol, 997 | M: RedisDeserializationProtocol + Hash + Eq, 998 | { 999 | let mut cmd = Command::new("SUNION"); 1000 | for key in keys { 1001 | cmd.arg(key); 1002 | } 1003 | let reply = self.execute(cmd)?; 1004 | >::deserialization(reply) 1005 | } 1006 | 1007 | /// This command is equal to SUNION, but instead of returning the resulting set, it is stored in destination. 1008 | /// 1009 | /// Return value: Integer reply 1010 | pub fn sunionstore(&mut self, destination: K, keys: Vec) -> RedisResult 1011 | where 1012 | K: RedisSerializationProtocol, 1013 | { 1014 | let mut cmd = command!("SUNIONSTORE"; args => destination); 1015 | for key in keys { 1016 | cmd.arg(key); 1017 | } 1018 | let reply = self.execute(cmd)?; 1019 | ::deserialization(reply) 1020 | } 1021 | 1022 | // Sorted Sets commands 1023 | // TODO: design data structure 1024 | /// Adds all the specified members with the specified scores to the sorted set stored at key. 1025 | /// 1026 | /// Return value: Integer reply 1027 | pub fn zadd(&mut self, key: K, paris: Vec<(isize, M)>) -> RedisResult 1028 | where 1029 | K: RedisSerializationProtocol, 1030 | M: RedisSerializationProtocol, 1031 | { 1032 | let mut cmd = command!("ZADD"; args => key); 1033 | for (score, member) in paris { 1034 | cmd.arg(score).arg(member); 1035 | } 1036 | let reply = self.execute(cmd)?; 1037 | ::deserialization(reply) 1038 | } 1039 | 1040 | /// Returns the sorted set cardinality (number of elements) of the sorted set stored at key. 1041 | /// 1042 | /// Return value: Integer reply 1043 | pub fn zcard(&mut self, key: K) -> RedisResult 1044 | where 1045 | K: RedisSerializationProtocol, 1046 | { 1047 | let cmd = command!("ZCARD"; args => key); 1048 | let reply = self.execute(cmd)?; 1049 | ::deserialization(reply) 1050 | } 1051 | 1052 | /// Returns the number of elements in the sorted set at key with a score between min and max. 1053 | /// 1054 | /// Return value: Integer reply 1055 | pub fn zcount(&mut self, key: K, min: isize, max: isize) -> RedisResult 1056 | where 1057 | K: RedisSerializationProtocol, 1058 | { 1059 | let cmd = command!("ZCOUNT"; args => key, min, max); 1060 | let reply = self.execute(cmd)?; 1061 | ::deserialization(reply) 1062 | } 1063 | 1064 | // Strings commands 1065 | pub fn append(&mut self, key: K, value: V) -> RedisResult 1066 | where 1067 | K: RedisSerializationProtocol, 1068 | V: RedisSerializationProtocol, 1069 | { 1070 | let cmd = command!("APPEND"; args => key, value); 1071 | let reply = self.execute(cmd)?; 1072 | ::deserialization(reply) 1073 | } 1074 | 1075 | /// Count the number of set bits (population counting) in a string. 1076 | pub fn bitcount(&mut self, key: K, start: Option, end: Option) -> RedisResult 1077 | where 1078 | K: RedisSerializationProtocol, 1079 | { 1080 | let mut cmd = command!("BITCOUNT"; args => key); 1081 | if let Some(start) = start { 1082 | cmd.arg(start); 1083 | } 1084 | if let Some(end) = end { 1085 | cmd.arg(end); 1086 | } 1087 | 1088 | let reply = self.execute(cmd)?; 1089 | ::deserialization(reply) 1090 | } 1091 | 1092 | /// Perform a bitwise operation between multiple keys (containing string values) and store the result in the destination key. 1093 | pub fn bitop(&mut self, operation: &str, destkey: K1, keys: Vec) -> RedisResult 1094 | where 1095 | K1: RedisSerializationProtocol, 1096 | K2: RedisSerializationProtocol, 1097 | { 1098 | let mut cmd = command!("BITOP"; args => operation, destkey); 1099 | for key in keys { 1100 | cmd.arg(key); 1101 | } 1102 | 1103 | let reply = self.execute(cmd)?; 1104 | ::deserialization(reply) 1105 | } 1106 | 1107 | /// Return the position of the first bit set to 1 or 0 in a string. 1108 | pub fn bitpos(&mut self, key: K, bit: u8, start: Option, end: Option) -> RedisResult 1109 | where 1110 | K: RedisSerializationProtocol, 1111 | { 1112 | if end.is_some() && start.is_none() { 1113 | return Err(RedisError::custom( 1114 | ErrorKind::ClientError, 1115 | "`start` shouldn't be none when `end` has given", 1116 | )); 1117 | } 1118 | 1119 | let mut cmd = command!("BITPOS"; args => key, bit); 1120 | if let Some(start) = start { 1121 | cmd.arg(start); 1122 | } 1123 | if let Some(end) = end { 1124 | cmd.arg(end); 1125 | } 1126 | 1127 | let reply = self.execute(cmd)?; 1128 | ::deserialization(reply) 1129 | } 1130 | 1131 | /// Decrements the number stored at key by one. 1132 | pub fn decr(&mut self, key: K) -> RedisResult 1133 | where 1134 | K: RedisSerializationProtocol, 1135 | { 1136 | let cmd = command!("DECR"; args => key); 1137 | let reply = self.execute(cmd)?; 1138 | ::deserialization(reply) 1139 | } 1140 | 1141 | /// Decrements the number stored at key by decrement. 1142 | pub fn decrby(&mut self, key: K, decrement: i64) -> RedisResult 1143 | where 1144 | K: RedisSerializationProtocol, 1145 | { 1146 | let cmd = command!("DECRBY"; args => key, decrement); 1147 | let reply = self.execute(cmd)?; 1148 | ::deserialization(reply) 1149 | } 1150 | 1151 | /// Get the value of key. 1152 | pub fn get(&mut self, key: K) -> RedisResult 1153 | where 1154 | K: RedisSerializationProtocol, 1155 | V: RedisDeserializationProtocol, 1156 | { 1157 | let cmd = command!("GET"; args => key); 1158 | let reply = self.execute(cmd)?; 1159 | ::deserialization(reply) 1160 | } 1161 | 1162 | /// Returns the bit value at offset in the string value stored at key. 1163 | pub fn getbit(&mut self, key: K, offset: i64) -> RedisResult 1164 | where 1165 | K: RedisSerializationProtocol, 1166 | { 1167 | let cmd = command!("GETBIT"; args => key, offset); 1168 | let reply = self.execute(cmd)?; 1169 | ::deserialization(reply) 1170 | } 1171 | 1172 | /// Returns the substring of the string value stored at key, determined by the offsets start and end (both are inclusive). 1173 | pub fn getrange(&mut self, key: K, start: i64, end: i64) -> RedisResult 1174 | where 1175 | K: RedisSerializationProtocol, 1176 | { 1177 | let cmd = command!("GETRANGE"; args => key, start, end); 1178 | let reply = self.execute(cmd)?; 1179 | ::deserialization(reply) 1180 | } 1181 | 1182 | /// Atomically sets key to value and returns the old value stored at key. 1183 | pub fn getset(&mut self, key: K, value: V) -> RedisResult 1184 | where 1185 | K: RedisSerializationProtocol, 1186 | V: ToString, 1187 | { 1188 | let cmd = command!("GETSET"; args => key, value.to_string()); 1189 | let reply = self.execute(cmd)?; 1190 | ::deserialization(reply) 1191 | } 1192 | 1193 | /// Increments the number stored at key by one. 1194 | pub fn incr(&mut self, key: K) -> RedisResult 1195 | where 1196 | K: RedisSerializationProtocol, 1197 | { 1198 | let cmd = command!("INCR"; args => key); 1199 | let reply = self.execute(cmd)?; 1200 | ::deserialization(reply) 1201 | } 1202 | 1203 | /// Increments the number stored at key by increment. 1204 | pub fn incrby(&mut self, key: K, increment: i64) -> RedisResult 1205 | where 1206 | K: RedisSerializationProtocol, 1207 | { 1208 | let cmd = command!("INCRBY"; args => key, increment); 1209 | let reply = self.execute(cmd)?; 1210 | ::deserialization(reply) 1211 | } 1212 | 1213 | /// Increment the string representing a floating point number stored at key by the specified increment. 1214 | pub fn incrbyfloat(&mut self, key: K, increment: f64) -> RedisResult 1215 | where 1216 | K: RedisSerializationProtocol, 1217 | { 1218 | let cmd = command!("INCRBYFLOAT"; args => key, increment); 1219 | let reply = self.execute(cmd)?; 1220 | ::deserialization(reply) 1221 | } 1222 | 1223 | /// Returns the values of all specified keys. 1224 | pub fn mget(&mut self, keys: Vec) -> RedisResult> 1225 | where 1226 | K: RedisSerializationProtocol, 1227 | V: RedisDeserializationProtocol, 1228 | { 1229 | let mut cmd = Command::new("MGET"); 1230 | for key in keys { 1231 | cmd.arg(key); 1232 | } 1233 | 1234 | let reply = self.execute(cmd)?; 1235 | >::deserialization(reply) 1236 | } 1237 | 1238 | /// Sets the given keys to their respective values. 1239 | pub fn mset(&mut self, kvs: Vec<(K, V)>) -> RedisResult<()> 1240 | where 1241 | K: RedisSerializationProtocol, 1242 | V: RedisSerializationProtocol, 1243 | { 1244 | let mut cmd = Command::new("MSET"); 1245 | for (k, v) in kvs { 1246 | cmd.arg(k).arg(v); 1247 | } 1248 | 1249 | let reply = self.execute(cmd)?; 1250 | <()>::deserialization(reply) 1251 | } 1252 | 1253 | /// Sets the given keys to their respective values. 1254 | pub fn msetnx(&mut self, kvs: Vec<(K, V)>) -> RedisResult<()> 1255 | where 1256 | K: RedisSerializationProtocol, 1257 | V: RedisSerializationProtocol, 1258 | { 1259 | let mut cmd = Command::new("MSETNX"); 1260 | for (k, v) in kvs { 1261 | cmd.arg(k).arg(v); 1262 | } 1263 | 1264 | let reply = self.execute(cmd)?; 1265 | <()>::deserialization(reply) 1266 | } 1267 | 1268 | /// PSETEX works exactly like SETEX with the sole difference that the expire time is specified in milliseconds instead of seconds. 1269 | pub fn psetex(&mut self, key: K, milliseconds: u64, value: V) -> RedisResult<()> 1270 | where 1271 | K: RedisSerializationProtocol, 1272 | V: RedisSerializationProtocol, 1273 | { 1274 | let cmd = command!("PSETEX"; args => key, milliseconds, value); 1275 | let reply = self.execute(cmd)?; 1276 | <()>::deserialization(reply) 1277 | } 1278 | 1279 | /// Set key to hold the string value. 1280 | pub fn set( 1281 | &mut self, 1282 | key: K, 1283 | value: V, 1284 | ex_seconds: Option, 1285 | px_milliseconds: Option, 1286 | nx: Option, 1287 | xx: Option, 1288 | ) -> RedisResult<()> 1289 | where 1290 | K: RedisSerializationProtocol, 1291 | V: RedisSerializationProtocol, 1292 | { 1293 | let mut cmd = command!("SET"; args => key, value); 1294 | if let Some(ex) = ex_seconds { 1295 | cmd.arg("EX").arg(ex); 1296 | } 1297 | if let Some(px) = px_milliseconds { 1298 | cmd.arg("PX").arg(px); 1299 | } 1300 | if let Some(nx) = nx { 1301 | if nx { 1302 | cmd.arg("NX"); 1303 | } 1304 | } 1305 | if let Some(xx) = xx { 1306 | if xx { 1307 | cmd.arg("XX"); 1308 | } 1309 | } 1310 | 1311 | let reply = self.execute(cmd)?; 1312 | <()>::deserialization(reply) 1313 | } 1314 | 1315 | /// Set key to hold the string value. 1316 | pub fn simple_set(&mut self, key: K, value: V) -> RedisResult<()> 1317 | where 1318 | K: RedisSerializationProtocol, 1319 | V: RedisSerializationProtocol, 1320 | { 1321 | self.set(key, value, None, None, None, None) 1322 | } 1323 | 1324 | /// Sets or clears the bit at offset in the string value stored at key. 1325 | pub fn setbit(&mut self, key: K, offset: usize, value: u8) -> RedisResult 1326 | where 1327 | K: RedisSerializationProtocol, 1328 | { 1329 | let cmd = command!("SETBIT"; args => key, offset, value); 1330 | let reply = self.execute(cmd)?; 1331 | ::deserialization(reply) 1332 | } 1333 | 1334 | /// Set key to hold the string value and set key to timeout after a given number of seconds. 1335 | pub fn setex(&mut self, key: K, seconds: usize, value: V) -> RedisResult<()> 1336 | where 1337 | K: RedisSerializationProtocol, 1338 | V: RedisSerializationProtocol, 1339 | { 1340 | let cmd = command!("SETEX"; args => key, seconds, value); 1341 | let reply = self.execute(cmd)?; 1342 | <()>::deserialization(reply) 1343 | } 1344 | 1345 | /// Set key to hold string value if key does not exist. 1346 | pub fn setnx(&mut self, key: K, value: V) -> RedisResult 1347 | where 1348 | K: RedisSerializationProtocol, 1349 | V: RedisSerializationProtocol, 1350 | { 1351 | let cmd = command!("SETNX"; args => key, value); 1352 | let reply = self.execute(cmd)?; 1353 | ::deserialization(reply) 1354 | } 1355 | 1356 | /// Overwrites part of the string stored at key, starting at the specified offset, for the entire length of value. 1357 | pub fn setrange(&mut self, key: K, offset: usize, value: V) -> RedisResult 1358 | where 1359 | K: RedisSerializationProtocol, 1360 | V: RedisSerializationProtocol, 1361 | { 1362 | let cmd = command!("SETRANGE"; args => key, offset, value); 1363 | let reply = self.execute(cmd)?; 1364 | ::deserialization(reply) 1365 | } 1366 | 1367 | /// Returns the length of the string value stored at key. 1368 | pub fn strlen(&mut self, key: K) -> RedisResult 1369 | where 1370 | K: RedisSerializationProtocol, 1371 | { 1372 | let cmd = command!("STRLEN"; args => key); 1373 | let reply = self.execute(cmd)?; 1374 | ::deserialization(reply) 1375 | } 1376 | 1377 | fn execute(&mut self, cmd: Command) -> RedisResult { 1378 | let mut conn = self.pool.get()?; 1379 | conn.send(&cmd.into_vec())?; 1380 | let reply = conn.receive()?; 1381 | self.pool.put(conn); 1382 | Ok(reply) 1383 | } 1384 | } 1385 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | pub trait ToRedisConnectionConfig { 2 | fn to_redis_connection_config(&self) -> RedisConfig; 3 | } 4 | 5 | // TODO: impl TORedisConnectionConfig for String, &str and so on. 6 | 7 | pub struct RedisConfig { 8 | pub address: String, 9 | pub database: u8, 10 | pub username: Option, 11 | pub password: Option, 12 | pub pool_capacity: usize, 13 | } 14 | 15 | impl RedisConfig { 16 | pub fn new( 17 | address: String, 18 | database: u8, 19 | username: Option, 20 | password: Option, 21 | pool_capacity: usize, 22 | ) -> Self { 23 | RedisConfig { 24 | address, 25 | database, 26 | username, 27 | password, 28 | pool_capacity, 29 | } 30 | } 31 | } 32 | 33 | impl Default for RedisConfig { 34 | fn default() -> Self { 35 | RedisConfig { 36 | address: "127.0.0.1:6379".to_string(), 37 | database: 0, 38 | username: None, 39 | password: None, 40 | pool_capacity: 8, 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/connection.rs: -------------------------------------------------------------------------------- 1 | use std::io::{BufRead, BufReader, Read, Write}; 2 | use std::net::{TcpStream, ToSocketAddrs}; 3 | 4 | use crate::error::ErrorKind::{AuthenticationFailed, FromServer}; 5 | use crate::error::{ErrorKind::ResponseError, RedisError}; 6 | use crate::RedisResult; 7 | 8 | pub(crate) struct Connection { 9 | conn: TcpStream, 10 | reader: BufReader, 11 | } 12 | 13 | impl Connection { 14 | const SINGLE_STRINGS: u8 = b'+'; 15 | const ERRORS: u8 = b'-'; 16 | const INTEGERS: u8 = b':'; 17 | const BULK_STRINGS: u8 = b'$'; 18 | const ARRAYS: u8 = b'*'; 19 | 20 | fn new(stream: TcpStream) -> RedisResult { 21 | let reader = BufReader::new(stream.try_clone()?); 22 | 23 | Ok(Connection { conn: stream, reader }) 24 | } 25 | 26 | pub(crate) fn connect(addr: A) -> RedisResult { 27 | let stream = TcpStream::connect(addr)?; 28 | 29 | Self::new(stream) 30 | } 31 | 32 | pub(crate) fn send(&mut self, data: &[u8]) -> RedisResult<()> { 33 | self.conn.write_all(data)?; 34 | Ok(()) 35 | } 36 | 37 | pub(crate) fn receive(&mut self) -> RedisResult { 38 | let mut buffer = Vec::new(); 39 | self.reader.read_until(b'\n', &mut buffer)?; 40 | if buffer.len() < 3 { 41 | return Err(RedisError::custom(ResponseError, "Empty redis response")); 42 | } 43 | if buffer == b"NIL" { 44 | // TODO: remove 45 | return Ok(Reply::Nil); 46 | } 47 | 48 | let prefix = buffer[0]; 49 | let buffer = &buffer[1..buffer.len() - 2]; // remove prefix and '\r\n' 50 | 51 | match prefix { 52 | Self::SINGLE_STRINGS => self.read_single_strings(Vec::from(buffer)), 53 | Self::ERRORS => self.read_errors(Vec::from(buffer)), 54 | Self::INTEGERS => self.read_integer(Vec::from(buffer)), 55 | Self::BULK_STRINGS => self.read_bulk_strings(String::from_utf8_lossy(buffer).parse::()?), 56 | Self::ARRAYS => self.read_array(String::from_utf8_lossy(buffer).parse::()?), 57 | _ => Err(RedisError::custom( 58 | ResponseError, 59 | format!("invalid prefix {:?}", prefix as char), 60 | )), 61 | } 62 | } 63 | 64 | fn read_single_strings(&mut self, buffer: Vec) -> RedisResult { 65 | // TODO 66 | if buffer == b"OK" { 67 | return Ok(Reply::SingleStrings(SingleStrings::Okay)); 68 | } else if buffer == b"PONG" { 69 | return Ok(Reply::SingleStrings(SingleStrings::Pong)); 70 | } else if buffer == b"string" { 71 | return Ok(Reply::SingleStrings(SingleStrings::String)); 72 | } else if buffer == b"list" { 73 | return Ok(Reply::SingleStrings(SingleStrings::List)); 74 | } else if buffer == b"set" { 75 | return Ok(Reply::SingleStrings(SingleStrings::Set)); 76 | } 77 | Ok(Reply::SingleStrings(SingleStrings::Okay)) 78 | } 79 | 80 | fn read_errors(&mut self, buffer: Vec) -> RedisResult { 81 | let error = String::from_utf8_lossy(&buffer).into_owned(); 82 | if error.starts_with("WRONGPASS") { 83 | return Err(RedisError::custom(AuthenticationFailed, error)); 84 | } 85 | Err(RedisError::custom(FromServer, error)) 86 | } 87 | 88 | fn read_integer(&mut self, buffer: Vec) -> RedisResult { 89 | Ok(Reply::Integers(buffer)) 90 | } 91 | 92 | fn read_bulk_strings(&mut self, size: i64) -> RedisResult { 93 | if size < 0 { 94 | // TODO 95 | return Ok(Reply::Nil); 96 | } 97 | 98 | let mut buf = vec![0; (size + 2) as usize]; 99 | self.reader.read_exact(&mut buf)?; 100 | buf.truncate(buf.len() - 2); 101 | Ok(Reply::BulkStrings(buf)) 102 | } 103 | 104 | fn read_array(&mut self, len: u64) -> RedisResult { 105 | let mut result = Vec::with_capacity(len as usize); 106 | for _ in 0..len { 107 | let mut buf = Vec::new(); 108 | self.reader.read_until(b'\n', &mut buf)?; 109 | let size = String::from_utf8_lossy(&buf[1..(buf.len() - 2)]).parse::()?; 110 | let v = self.read_bulk_strings(size)?; 111 | result.push(v); 112 | } 113 | 114 | Ok(Reply::Arrays(result)) 115 | } 116 | } 117 | 118 | #[derive(Debug, Clone)] 119 | pub enum SingleStrings { 120 | Okay, 121 | Pong, 122 | String, 123 | List, 124 | Set, 125 | } 126 | 127 | #[derive(Debug, Clone)] 128 | pub enum Reply { 129 | SingleStrings(SingleStrings), 130 | Integers(Vec), 131 | BulkStrings(Vec), 132 | Arrays(Vec), 133 | Nil, 134 | } 135 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | /// An enum of all error kinds. 4 | #[derive(PartialEq, Eq, Copy, Clone, Debug)] 5 | pub enum ErrorKind { 6 | ResponseError, 7 | AuthenticationFailed, 8 | TypeError, 9 | Io, 10 | ClientError, 11 | FromServer, 12 | } 13 | 14 | enum Repr { 15 | Io(std::io::Error), 16 | Custom(ErrorKind, Cow<'static, str>), 17 | } 18 | 19 | pub struct RedisError { 20 | repr: Repr, 21 | } 22 | 23 | impl std::fmt::Display for RedisError { 24 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 25 | match self.repr { 26 | Repr::Io(ref e) => e.fmt(f), 27 | Repr::Custom(_, ref desc) => write!(f, "{}", desc), 28 | } 29 | } 30 | } 31 | 32 | impl std::fmt::Debug for RedisError { 33 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 34 | std::fmt::Display::fmt(self, f) 35 | } 36 | } 37 | 38 | impl std::error::Error for RedisError {} 39 | 40 | impl From for RedisError { 41 | fn from(e: std::io::Error) -> Self { 42 | RedisError { repr: Repr::Io(e) } 43 | } 44 | } 45 | 46 | impl From for RedisError { 47 | fn from(_: std::num::ParseIntError) -> Self { 48 | RedisError { 49 | repr: Repr::Custom(ErrorKind::TypeError, Cow::from("invalid digit")), 50 | } 51 | } 52 | } 53 | 54 | impl From for RedisError { 55 | fn from(_: std::num::ParseFloatError) -> Self { 56 | RedisError { 57 | repr: Repr::Custom(ErrorKind::TypeError, Cow::from("invalid float")), 58 | } 59 | } 60 | } 61 | 62 | impl From for RedisError { 63 | fn from(_: std::string::FromUtf8Error) -> Self { 64 | RedisError { 65 | repr: Repr::Custom(ErrorKind::TypeError, Cow::from("invalid utf-8")), 66 | } 67 | } 68 | } 69 | 70 | impl From for RedisError { 71 | fn from(_: std::str::Utf8Error) -> Self { 72 | RedisError { 73 | repr: Repr::Custom(ErrorKind::TypeError, Cow::from("invalid utf-8")), 74 | } 75 | } 76 | } 77 | 78 | impl RedisError { 79 | pub fn custom(kind: ErrorKind, desc: S) -> RedisError { 80 | RedisError { 81 | repr: Repr::Custom(kind, Cow::from(desc.to_string())), 82 | } 83 | } 84 | } 85 | 86 | impl RedisError { 87 | pub fn kind(&self) -> ErrorKind { 88 | match self.repr { 89 | Repr::Io(_) => ErrorKind::Io, 90 | Repr::Custom(kind, _) => kind, 91 | } 92 | } 93 | 94 | pub fn is_io_error(&self) -> bool { 95 | self.as_io_error().is_some() 96 | } 97 | 98 | pub fn as_io_error(&self) -> Option<&std::io::Error> { 99 | match self.repr { 100 | Repr::Io(ref e) => Some(e), 101 | _ => None, 102 | } 103 | } 104 | 105 | pub fn is_connection_refuse(&self) -> bool { 106 | match self.repr { 107 | Repr::Io(ref e) => matches!( 108 | e.kind(), 109 | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::NotFound 110 | ), 111 | _ => false, 112 | } 113 | } 114 | 115 | pub fn is_timeout(&self) -> bool { 116 | match self.repr { 117 | Repr::Io(ref e) => matches!(e.kind(), std::io::ErrorKind::TimedOut | std::io::ErrorKind::WouldBlock), 118 | _ => false, 119 | } 120 | } 121 | 122 | pub fn is_connection_dropped(&self) -> bool { 123 | match self.repr { 124 | Repr::Io(ref e) => matches!( 125 | e.kind(), 126 | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::BrokenPipe 127 | ), 128 | _ => false, 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! ```toml 2 | //! [dependencies.redisclient] 3 | //! version = "*" 4 | //! ``` 5 | //! 6 | //! If you want use the Git version: 7 | //! 8 | //! ```toml 9 | //! [dependencies.redisclient] 10 | //! git = "https://github.com/ltoddy/redis-rs.git" 11 | //! ``` 12 | //! 13 | //! ### simple usage 14 | //! 15 | //! ```rust 16 | //! extern crate redisclient; 17 | //! 18 | //! use redisclient::RedisResult; 19 | //! use redisclient::RedisClient; 20 | //! 21 | //! fn run() -> RedisResult<()> { 22 | //! let mut client = RedisClient::new()?; 23 | //! client.mset(vec![("key1", 1), ("key2", 2)])?; 24 | //! 25 | //! let values: Vec = client.mget(vec!["key1", "key2"])?; 26 | //! println!("values -> {:?}", values); 27 | //! 28 | //! let values: Vec = client.mget(vec!["key1", "key2"])?; 29 | //! println!("values -> {:?}", values); 30 | //! 31 | //! Ok(()) 32 | //! } 33 | //! ``` 34 | 35 | pub mod client; 36 | pub mod config; 37 | pub mod connection; 38 | pub mod error; 39 | pub mod macros; 40 | pub mod pipeline; 41 | pub mod pool; 42 | pub mod protocol; 43 | 44 | pub use client::RedisClient; 45 | pub use error::{ErrorKind, RedisError}; 46 | 47 | pub type RedisResult = std::result::Result; 48 | 49 | #[derive(Debug, Copy, Clone, PartialEq)] 50 | pub enum DataType { 51 | String, 52 | List, 53 | Set, 54 | } 55 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! hash_map { 3 | () => { std::collections::HashMap::with_capacity(16) }; 4 | 5 | ($key: expr => $value: expr) => { 6 | hash_map!($key => $value; 16) 7 | }; 8 | ($key: expr => $value: expr; $init_capacity: expr) => { 9 | { 10 | let mut hash_map = std::collections::HashMap::with_capacity($init_capacity); 11 | hash_map.insert($key, $value); 12 | hash_map 13 | } 14 | }; 15 | 16 | ($($key: expr => $value: expr),*) => { 17 | vec![$(($key, $value)),*].into_iter().collect::>() 18 | }; 19 | ($($key: expr => $value: expr,)*) => { 20 | hash_map!($($key => $value),*) 21 | }; 22 | } 23 | 24 | #[macro_export] 25 | macro_rules! btree_map { 26 | () => { std::collections::BTreeMap::new() }; 27 | 28 | ($key: expr => $value: expr) => { 29 | let mut map = std::collections::BTreeMap::new(); 30 | map.insert($key, $value); 31 | map 32 | }; 33 | 34 | ($($key: expr => $value: expr),*) => { 35 | { 36 | use std::iter::FromIterator; 37 | BTreeMap::from_iter(vec![$(($key, $value)),*]) 38 | } 39 | }; 40 | ($($key: expr => $value: expr,)*) => { 41 | btree_map!($($key => $value),*) 42 | }; 43 | } 44 | 45 | #[macro_export] 46 | macro_rules! hash_set { 47 | () => { std::collections::HashSet::new() }; 48 | 49 | ($($elements: expr),*) => { 50 | { 51 | let mut set = hash_set!(); 52 | $(set.insert($elements);)* 53 | set 54 | } 55 | }; 56 | 57 | ($($elements: expr,)*) => { 58 | hash_set!($($elements),*) 59 | }; 60 | } 61 | 62 | #[macro_export] 63 | macro_rules! btree_set { 64 | () => { std::collections::BTreeSet::new() }; 65 | 66 | ($($elements: expr),*) => { 67 | { 68 | let mut set = btree_set!(); 69 | $(set.insert($elements);)* 70 | set 71 | } 72 | }; 73 | 74 | ($($elements: expr,)*) => { 75 | btree_set!($($elements),*) 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /src/pipeline.rs: -------------------------------------------------------------------------------- 1 | use crate::client::Command; 2 | use crate::RedisClient; 3 | 4 | #[allow(dead_code)] 5 | pub struct Pipeline<'a> { 6 | client: &'a RedisClient, // TODO 7 | commands: Vec, 8 | transaction: bool, 9 | } 10 | 11 | impl<'a> Pipeline<'a> { 12 | pub(crate) fn new(client: &RedisClient) -> Pipeline { 13 | Self::with_capacity(client, 0) 14 | } 15 | 16 | pub(crate) fn with_capacity(client: &RedisClient, capacity: usize) -> Pipeline { 17 | Pipeline { 18 | client, 19 | commands: Vec::with_capacity(capacity), 20 | transaction: false, 21 | } 22 | } 23 | 24 | pub fn transaction_mode(&'a mut self) -> &mut Pipeline { 25 | self.transaction = true; 26 | self 27 | } 28 | 29 | pub fn clear(&mut self) { 30 | self.commands.clear(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/pool.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | 3 | use crate::connection::Connection; 4 | use crate::error::{ErrorKind, RedisError}; 5 | use crate::RedisResult; 6 | 7 | pub struct ConnectionPool { 8 | addr: String, 9 | capacity: usize, 10 | idles: VecDeque, 11 | closed: bool, 12 | } 13 | 14 | impl ConnectionPool { 15 | pub(super) fn new(capacity: usize, address: String) -> ConnectionPool { 16 | ConnectionPool { 17 | addr: address, 18 | capacity, 19 | idles: VecDeque::with_capacity(capacity), 20 | closed: false, 21 | } 22 | } 23 | 24 | pub(super) fn get(&mut self) -> RedisResult { 25 | if self.closed { 26 | return Err(RedisError::custom(ErrorKind::ClientError, "Connection pool closed")); 27 | } 28 | 29 | if let Some(conn) = self.idles.pop_front() { 30 | return Ok(conn); 31 | } 32 | Connection::connect(&self.addr) 33 | } 34 | 35 | pub(super) fn put(&mut self, conn: Connection) { 36 | if self.closed { 37 | return; 38 | } 39 | 40 | if self.idles.len() >= self.capacity { 41 | let _ = self.idles.pop_front(); 42 | } 43 | self.idles.push_back(conn); 44 | } 45 | 46 | fn close(&mut self) { 47 | self.closed = true; 48 | 49 | while let Some(conn) = self.idles.pop_front() { 50 | drop(conn); 51 | } 52 | } 53 | } 54 | 55 | impl Drop for ConnectionPool { 56 | fn drop(&mut self) { 57 | self.close() 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/protocol/maps.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! implement_serialization_for_maps { 3 | ($($t:ident),*) => { 4 | $( 5 | impl RedisSerializationProtocol for $t 6 | where 7 | K: RedisSerializationProtocol + Hash + Eq, 8 | V: RedisSerializationProtocol, 9 | { 10 | fn serialization(&self) -> Vec { 11 | unimplemented!() 12 | } 13 | } 14 | )* 15 | }; 16 | } 17 | 18 | #[macro_export] 19 | macro_rules! implement_deserialization_for_maps { 20 | ($($t:ident),*) => { 21 | $( 22 | impl RedisDeserializationProtocol for $t:: 23 | where 24 | K: RedisDeserializationProtocol + Eq + Hash + Ord, 25 | V: RedisDeserializationProtocol, 26 | { 27 | fn deserialization(reply: Reply) -> RedisResult { 28 | // TODO: ugly code, refactor !!! 29 | match reply { 30 | Reply::Arrays(array) => { 31 | let hash = array 32 | .chunks(2) 33 | .map(|chunk| { 34 | let field = &chunk[0]; 35 | let value = &chunk[1]; 36 | ( 37 | ::deserialization(field.clone()).unwrap(), 38 | ::deserialization(value.clone()).unwrap(), // TODO: remove clone and unwrap 39 | ) 40 | }) 41 | .collect(); 42 | Ok(hash) 43 | } 44 | _ => Err(RedisError::custom(TypeError, "miss type")), 45 | } 46 | } 47 | } 48 | )* 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | mod maps; 2 | mod numbers; 3 | mod sequences; 4 | mod sets; 5 | mod strings; 6 | 7 | use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; 8 | use std::hash::Hash; 9 | 10 | use crate::client::ListBeforeOrAfter; 11 | use crate::connection::{Reply, SingleStrings}; 12 | use crate::error::ErrorKind::{ResponseError, TypeError}; 13 | use crate::error::RedisError; 14 | use crate::implement_deserialization_for_maps; 15 | use crate::implement_deserialization_for_numbers; 16 | use crate::implement_deserialization_for_sets; 17 | use crate::implement_deserialization_for_string; 18 | use crate::implement_serialization_for_maps; 19 | use crate::implement_serialization_for_numbers; 20 | use crate::implement_serialization_for_sets; 21 | use crate::implement_serialization_for_string; 22 | use crate::DataType; 23 | use crate::RedisResult; 24 | 25 | pub trait RedisSerializationProtocol { 26 | fn serialization(&self) -> Vec; 27 | } 28 | 29 | pub trait RedisDeserializationProtocol { 30 | fn deserialization(reply: Reply) -> RedisResult 31 | where 32 | Self: Sized; 33 | } 34 | 35 | // --------------------------------------- 36 | 37 | implement_deserialization_for_string!(String); 38 | implement_serialization_for_string!(String, &str); 39 | 40 | implement_serialization_for_numbers!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64); 41 | implement_deserialization_for_numbers!(u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64); 42 | 43 | implement_serialization_for_maps!(HashMap, BTreeMap); 44 | implement_deserialization_for_maps!(HashMap, BTreeMap); 45 | 46 | implement_serialization_for_sets!(HashSet, BTreeSet); 47 | implement_deserialization_for_sets!(HashSet); // TODO: BTreeSet, Ord trait 48 | 49 | impl RedisDeserializationProtocol for () { 50 | fn deserialization(reply: Reply) -> RedisResult { 51 | match reply { 52 | Reply::SingleStrings(single) => match single { 53 | SingleStrings::Okay | SingleStrings::Pong => Ok(()), 54 | _ => Err(RedisError::custom(ResponseError, "wrong data type")), 55 | }, 56 | _ => Err(RedisError::custom(TypeError, "miss type")), 57 | } 58 | } 59 | } 60 | 61 | impl RedisDeserializationProtocol for bool { 62 | fn deserialization(reply: Reply) -> RedisResult { 63 | let v = ::deserialization(reply)?; 64 | Ok(v != 0) 65 | } 66 | } 67 | 68 | impl RedisDeserializationProtocol for DataType { 69 | fn deserialization(reply: Reply) -> RedisResult { 70 | match reply { 71 | Reply::SingleStrings(single) => match single { 72 | SingleStrings::String => Ok(DataType::String), 73 | SingleStrings::List => Ok(DataType::List), 74 | SingleStrings::Set => Ok(DataType::Set), 75 | _ => Err(RedisError::custom(ResponseError, "wrong data type")), 76 | }, 77 | _ => Err(RedisError::custom(TypeError, "miss type")), 78 | } 79 | } 80 | } 81 | 82 | macro_rules! implement_serialization_for_sequences { 83 | ($($t:ty),*) => { 84 | $( 85 | impl RedisSerializationProtocol for $t { 86 | fn serialization(&self) -> Vec { 87 | let length = self.len(); 88 | let mut buf = Vec::new(); 89 | buf.extend(b"*1\r\n"); 90 | buf.extend(format!("${}\r\n", length).as_bytes()); 91 | buf.extend(self); 92 | buf.extend(b"\r\n"); 93 | buf 94 | } 95 | } 96 | )* 97 | }; 98 | } 99 | 100 | implement_serialization_for_sequences!(Vec); 101 | 102 | impl RedisSerializationProtocol for ListBeforeOrAfter { 103 | fn serialization(&self) -> Vec { 104 | match self { 105 | ListBeforeOrAfter::Before => "BEFORE".serialization(), 106 | ListBeforeOrAfter::After => "AFTER".serialization(), 107 | } 108 | } 109 | } 110 | 111 | impl RedisDeserializationProtocol for Vec 112 | where 113 | T: RedisDeserializationProtocol, 114 | { 115 | fn deserialization(reply: Reply) -> RedisResult { 116 | match reply { 117 | Reply::Arrays(array) => { 118 | let mut values = Vec::new(); 119 | for ele in array { 120 | values.push(::deserialization(ele)?); 121 | } 122 | Ok(values) 123 | } 124 | _ => Err(RedisError::custom(TypeError, "miss type")), 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/protocol/numbers.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! implement_serialization_for_numbers { 3 | ($($t:ty),*) => { 4 | $( 5 | impl RedisSerializationProtocol for $t { 6 | fn serialization(&self) -> Vec { 7 | let s = format!("{}", self); 8 | let length = s.len(); 9 | let mut buf = Vec::new(); 10 | buf.extend(format!("${}\r\n", length).as_bytes()); 11 | buf.extend(s.as_bytes()); 12 | buf.extend(b"\r\n"); 13 | buf 14 | } 15 | } 16 | )* 17 | }; 18 | } 19 | 20 | #[macro_export] 21 | macro_rules! implement_deserialization_for_numbers { 22 | ($($t:ty),*) => { 23 | $( 24 | impl RedisDeserializationProtocol for $t { 25 | fn deserialization(reply: Reply) -> RedisResult { 26 | match reply { 27 | Reply::Integers(data) => Ok(String::from_utf8(data)?.parse::<$t>()?), 28 | Reply::BulkStrings(data) => Ok(String::from_utf8(data)?.parse::<$t>()?), 29 | _ => Err(RedisError::custom(TypeError, "miss type")), 30 | } 31 | } 32 | } 33 | )* 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /src/protocol/sequences.rs: -------------------------------------------------------------------------------- 1 | // TODO 2 | #[macro_export] 3 | macro_rules! implement_serialization_for_sequences { 4 | ($($t: ident),*) => { 5 | $( 6 | impl RedisSerializationProtocol for $t:: 7 | where 8 | T: RedisSerializationProtocol, 9 | { 10 | fn serialization(&self) -> Vec { 11 | let length = self.len(); 12 | let mut buf = Vec::new(); 13 | buf.extend(b"1\r\n"); 14 | buf.extend(format!("${}\r\n", length).as_bytes()); 15 | for ele in self { 16 | buf.extend(ele.serialization()); 17 | } 18 | buf 19 | } 20 | } 21 | )* 22 | }; 23 | } 24 | 25 | #[macro_export] 26 | macro_rules! implement_deserialization_for_sequences { 27 | ($($t: ident),*) => {}; 28 | } 29 | -------------------------------------------------------------------------------- /src/protocol/sets.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! implement_serialization_for_sets { 3 | ($($t:ident),*) => { 4 | $( 5 | impl RedisSerializationProtocol for $t 6 | where 7 | T: RedisSerializationProtocol + Hash + Eq, 8 | { 9 | fn serialization(&self) -> Vec { 10 | let length = self.len(); 11 | let mut buf = Vec::new(); 12 | buf.extend(b"1\r\n"); 13 | buf.extend(format!("${}\r\n", length).as_bytes()); 14 | for member in self { 15 | buf.extend(member.serialization()); 16 | } 17 | buf 18 | } 19 | } 20 | )* 21 | }; 22 | } 23 | 24 | #[macro_export] 25 | macro_rules! implement_deserialization_for_sets { 26 | ($($t:ident),*) => { 27 | $( 28 | impl RedisDeserializationProtocol for $t 29 | where 30 | T: RedisDeserializationProtocol + Hash + Eq, 31 | { 32 | fn deserialization(reply: Reply) -> RedisResult { 33 | match reply { 34 | Reply::Arrays(array) => { 35 | let mut set = $t::::new(); 36 | for reply in array { 37 | let element = ::deserialization(reply)?; 38 | set.insert(element); 39 | } 40 | Ok(set) 41 | } 42 | _ => Err(RedisError::custom(TypeError, "miss type")), 43 | } 44 | } 45 | } 46 | )* 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /src/protocol/strings.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! implement_serialization_for_string { 3 | ($($t:ty),*) => { 4 | $( 5 | impl RedisSerializationProtocol for $t { 6 | fn serialization(&self) -> Vec { 7 | let length = self.len(); 8 | let mut buf = Vec::new(); 9 | buf.extend(format!("${}\r\n", length).as_bytes()); 10 | buf.extend(self.as_bytes()); 11 | buf.extend(b"\r\n"); 12 | buf 13 | } 14 | } 15 | )* 16 | }; 17 | } 18 | 19 | #[macro_export] 20 | macro_rules! implement_deserialization_for_string { 21 | ($($t:ty),*) => { 22 | $( 23 | impl RedisDeserializationProtocol for $t { 24 | fn deserialization(reply: Reply) -> RedisResult { 25 | match reply { 26 | Reply::SingleStrings(single) => { 27 | match single { SingleStrings::Okay | SingleStrings::Pong => Ok(<$t>::new()), _ => Err(RedisError::custom(ResponseError, "wrong data type")) } 28 | }, 29 | Reply::BulkStrings(data) => Ok(<$t>::from_utf8(data)?), 30 | Reply::Nil => Ok(<$t>::new()), 31 | _ => Err(RedisError::custom(TypeError, "miss type")), 32 | } 33 | } 34 | } 35 | )* 36 | }; 37 | } 38 | -------------------------------------------------------------------------------- /tests/test_connection_commands.rs: -------------------------------------------------------------------------------- 1 | use redisclient::RedisClient; 2 | 3 | #[test] 4 | pub fn test_echo() { 5 | let mut client = RedisClient::new().unwrap(); 6 | 7 | let resp = client.echo("Hello world").unwrap(); 8 | assert_eq!(resp, "Hello world".to_string()); 9 | } 10 | 11 | #[test] 12 | pub fn test_ping() { 13 | let mut client = RedisClient::new().unwrap(); 14 | 15 | let _ = client.ping().unwrap(); 16 | } 17 | -------------------------------------------------------------------------------- /tests/test_hashes_commands.rs: -------------------------------------------------------------------------------- 1 | use redisclient::RedisClient; 2 | use redisclient::{btree_map, hash_map}; 3 | 4 | use std::collections::{BTreeMap, HashMap}; 5 | 6 | #[test] 7 | pub fn test_hel() { 8 | let mut client = RedisClient::new().unwrap(); 9 | 10 | client.hset("myhash", "field1", "foo").unwrap(); 11 | 12 | let res = client.hdel("myhash", vec!["field1"]).unwrap(); 13 | assert_eq!(res, 1); 14 | 15 | let res = client.hdel("myhash", vec!["field1"]).unwrap(); 16 | assert_eq!(res, 0); 17 | 18 | client.flushall().unwrap(); 19 | } 20 | 21 | #[test] 22 | pub fn test_hexists() { 23 | let mut client = RedisClient::new().unwrap(); 24 | 25 | client.hset("myhash", "field1", "foo").unwrap(); 26 | let exist = client.hexists("myhash", "field1").unwrap(); 27 | assert_eq!(exist, true); 28 | 29 | let exist = client.hexists("myhash", "field0").unwrap(); 30 | assert_eq!(exist, false); 31 | 32 | client.flushall().unwrap(); 33 | } 34 | 35 | #[test] 36 | pub fn test_hget() { 37 | let mut client = RedisClient::new().unwrap(); 38 | 39 | client.hset("myhash", "field1", "foo").unwrap(); 40 | 41 | let value: String = client.hget("myhash", "field1").unwrap(); 42 | assert_eq!(value, String::from("foo")); 43 | 44 | let value: String = client.hget("myhash", "field2").unwrap(); 45 | assert_eq!(value, String::new()); 46 | 47 | client.flushall().unwrap(); 48 | } 49 | 50 | #[test] 51 | pub fn test_hgetall() { 52 | let mut client = RedisClient::new().unwrap(); 53 | client.hset("myhash", "field1", "Hello").unwrap(); 54 | client.hset("myhash", "field2", "World").unwrap(); 55 | 56 | let hash: HashMap = client.hgetall("myhash").unwrap(); 57 | 58 | assert_eq!( 59 | hash, 60 | hash_map! { 61 | "field1".to_string() => "Hello".to_string(), 62 | "field2".to_string() => "World".to_string(), 63 | } 64 | ); 65 | 66 | let map: BTreeMap = client.hgetall("myhash").unwrap(); 67 | assert_eq!( 68 | map, 69 | btree_map! { 70 | "field1".to_string() => "Hello".to_string(), 71 | "field2".to_string() => "World".to_string(), 72 | } 73 | ); 74 | 75 | client.flushall().unwrap(); 76 | } 77 | 78 | #[test] 79 | pub fn test_hincrby() { 80 | let mut client = RedisClient::new().unwrap(); 81 | client.hset("myhash", "field", 5).unwrap(); 82 | 83 | let value = client.hincrby("myhash", "field", 1).unwrap(); 84 | assert_eq!(value, 6); 85 | 86 | let value = client.hincrby("myhash", "field", -1).unwrap(); 87 | assert_eq!(value, 5); 88 | 89 | let value = client.hincrby("myhash", "field", -10).unwrap(); 90 | assert_eq!(value, -5); 91 | 92 | client.flushall().unwrap(); 93 | } 94 | 95 | #[test] 96 | pub fn test_hincrbyfloat() { 97 | let mut client = RedisClient::new().unwrap(); 98 | client.hset("mykey", "field", 10.50).unwrap(); 99 | 100 | let value = client.hincrbyfloat("mykey", "field", 0.1).unwrap(); 101 | assert!((value - 10.6).abs() < f64::EPSILON); 102 | 103 | let value = client.hincrbyfloat("mykey", "field", -5_f64).unwrap(); 104 | assert!((value - 5.6).abs() < f64::EPSILON); 105 | 106 | client.hset("mykey", "field", 5.0e3).unwrap(); 107 | let value = client.hincrbyfloat("mykey", "field", 2.0e2).unwrap(); 108 | assert!((value - 5200_f64).abs() < f64::EPSILON); 109 | 110 | client.flushall().unwrap(); 111 | } 112 | 113 | #[test] 114 | pub fn test_hkeys() { 115 | let mut client = RedisClient::new().unwrap(); 116 | 117 | client 118 | .hmset("myhash", vec![("field1", "Hello"), ("field2", "World")]) 119 | .unwrap(); 120 | 121 | let keys: Vec = client.hkeys("myhash").unwrap(); 122 | assert_eq!(keys, vec!["field1".to_string(), "field2".to_string()]); 123 | 124 | client.flushall().unwrap(); 125 | } 126 | 127 | #[test] 128 | pub fn test_hlen() { 129 | let mut client = RedisClient::new().unwrap(); 130 | 131 | client 132 | .hmset("myhash", vec![("field1", "Hello"), ("field2", "World")]) 133 | .unwrap(); 134 | 135 | let len = client.hlen("myhash").unwrap(); 136 | 137 | assert_eq!(len, 2); 138 | 139 | client.flushall().unwrap(); 140 | } 141 | 142 | #[test] 143 | pub fn test_hmget() { 144 | let mut client = RedisClient::new().unwrap(); 145 | 146 | client 147 | .hmset("myhash", vec![("field1", "Hello"), ("field2", "World")]) 148 | .unwrap(); 149 | 150 | let values: Vec = client.hmget("myhash", vec!["field1", "field2", "nofield"]).unwrap(); 151 | 152 | assert_eq!(values, vec!["Hello".to_string(), "World".to_string(), String::new()]); 153 | 154 | client.flushall().unwrap(); 155 | } 156 | 157 | #[test] 158 | pub fn test_hmset() { 159 | let mut client = RedisClient::new().unwrap(); 160 | 161 | client 162 | .hmset("myhash", vec![("field1", "Hello"), ("field2", "World")]) 163 | .unwrap(); 164 | 165 | let value: String = client.hget("myhash", "field1").unwrap(); 166 | assert_eq!(value, "Hello".to_string()); 167 | 168 | let value: String = client.hget("myhash", "field2").unwrap(); 169 | assert_eq!(value, "World".to_string()); 170 | 171 | client.flushall().unwrap(); 172 | } 173 | 174 | #[test] 175 | pub fn test_hscan() {} 176 | 177 | #[test] 178 | pub fn test_hset() { 179 | let mut client = RedisClient::new().unwrap(); 180 | 181 | let amount = client.hset("myhash", "field1", "Hello").unwrap(); 182 | assert_eq!(amount, 1); 183 | 184 | let value: String = client.hget("myhash", "field1").unwrap(); 185 | assert_eq!(value, String::from("Hello")); 186 | 187 | client.flushall().unwrap(); 188 | } 189 | 190 | #[test] 191 | pub fn test_hsetnx() { 192 | let mut client = RedisClient::new().unwrap(); 193 | 194 | let amount = client.hsetnx("myhash", "field", "Hello").unwrap(); 195 | assert_eq!(amount, 1); 196 | 197 | let amount = client.hsetnx("myhash", "field", "World").unwrap(); 198 | assert_eq!(amount, 0); 199 | 200 | let value: String = client.hget("myhash", "field").unwrap(); 201 | assert_eq!(value, String::from("Hello")); 202 | 203 | client.flushall().unwrap(); 204 | } 205 | 206 | #[test] 207 | pub fn test_hstrlen() { 208 | let mut client = RedisClient::new().unwrap(); 209 | 210 | client 211 | .hmset("myhash", vec![("f1", "HelloWorld"), ("f2", "99"), ("f3", "-256")]) 212 | .unwrap(); 213 | 214 | let len = client.hstrlen("myhash", "f1").unwrap(); 215 | assert_eq!(len, 10); 216 | 217 | let len = client.hstrlen("myhash", "f2").unwrap(); 218 | assert_eq!(len, 2); 219 | 220 | let len = client.hstrlen("myhash", "f3").unwrap(); 221 | assert_eq!(len, 4); 222 | 223 | client.flushall().unwrap(); 224 | } 225 | 226 | #[test] 227 | pub fn test_hvals() { 228 | let mut client = RedisClient::new().unwrap(); 229 | 230 | client.hset("myhash", "field1", "Hello").unwrap(); 231 | client.hset("myhash", "field2", "World").unwrap(); 232 | 233 | let values: Vec = client.hvals("myhash").unwrap(); 234 | assert_eq!(values, vec!["Hello".to_string(), "World".to_string()]); 235 | 236 | client.flushall().unwrap(); 237 | } 238 | -------------------------------------------------------------------------------- /tests/test_keys_commands.rs: -------------------------------------------------------------------------------- 1 | use redisclient::{DataType, RedisClient}; 2 | 3 | #[test] 4 | pub fn test_del() { 5 | let mut client = RedisClient::new().unwrap(); 6 | client.simple_set("key1", "Hello").unwrap(); 7 | client.simple_set("key2", "World").unwrap(); 8 | 9 | let amount = client.del(vec!["key1", "key2"]).unwrap(); 10 | 11 | assert_eq!(amount, 2); 12 | 13 | client.flushall().unwrap(); 14 | } 15 | 16 | // #[test] 17 | // pub fn test_dump() { 18 | // let mut client = RedisClient::new().unwrap(); 19 | // 20 | // client.simple_set("mykey", 10).unwrap(); 21 | // let value = client.dump("mykey").unwrap(); 22 | // 23 | // assert_eq!(value, r#"\u0000\xC0\n\t\u0000\xBEm\u0006\x89Z(\u0000\n"#); 24 | // 25 | // client.flushall().unwrap(); 26 | // } 27 | 28 | #[test] 29 | pub fn test_exists() { 30 | let mut client = RedisClient::new().unwrap(); 31 | client.simple_set("key1", "Hello").unwrap(); 32 | 33 | let exist = client.exists(vec!["key1"]).unwrap(); 34 | assert_eq!(exist, 1); 35 | 36 | let exist = client.exists(vec!["nosuckkey"]).unwrap(); 37 | assert_eq!(exist, 0); 38 | 39 | client.simple_set("key2", "World").unwrap(); 40 | 41 | let exist = client.exists(vec!["key1", "key2", "nosuckkey"]).unwrap(); 42 | assert_eq!(exist, 2); 43 | 44 | client.flushall().unwrap(); 45 | } 46 | 47 | #[test] 48 | pub fn test_expire() { 49 | let mut client = RedisClient::new().unwrap(); 50 | 51 | client.simple_set("mykey", "Hello").unwrap(); 52 | assert!(client.expire("mykey", 10).unwrap()); 53 | 54 | let rest = client.ttl("mykey").unwrap(); 55 | assert_eq!(rest, 10); 56 | 57 | client.simple_set("mykey", "Hello world").unwrap(); 58 | let rest = client.ttl("mykey").unwrap(); 59 | assert_eq!(rest, -1); 60 | 61 | client.flushall().unwrap(); 62 | } 63 | 64 | // #[test] 65 | // pub fn test_expireat() {} 66 | 67 | #[test] 68 | pub fn test_keys() { 69 | let mut client = RedisClient::new().unwrap(); 70 | 71 | client 72 | .mset(vec![("firstname", "Jack"), ("lastname", "Stuntman"), ("age", "35")]) 73 | .unwrap(); 74 | 75 | let mut keys = client.keys("*name*").unwrap(); 76 | keys.sort(); 77 | assert_eq!(keys, vec!["firstname".to_string(), "lastname".to_string()]); 78 | 79 | let keys = client.keys("a??").unwrap(); 80 | assert_eq!(keys, vec!["age".to_string()]); 81 | 82 | let mut keys = client.keys("*").unwrap(); 83 | keys.sort(); 84 | assert_eq!( 85 | keys, 86 | vec!["age".to_string(), "firstname".to_string(), "lastname".to_string()] 87 | ); 88 | 89 | client.flushall().unwrap(); 90 | } 91 | 92 | #[test] 93 | pub fn test_persist() { 94 | let mut client = RedisClient::new().unwrap(); 95 | 96 | client.simple_set("mykey", "Hello").unwrap(); 97 | client.expire("mykey", 10).unwrap(); 98 | 99 | let rest = client.ttl("mykey").unwrap(); 100 | assert_eq!(rest, 10); 101 | 102 | client.persist("mykey").unwrap(); 103 | let rest = client.ttl("mykey").unwrap(); 104 | assert_eq!(rest, -1); 105 | 106 | client.flushall().unwrap(); 107 | } 108 | 109 | #[test] 110 | #[ignore] 111 | pub fn test_pexpire() { 112 | let mut client = RedisClient::new().unwrap(); 113 | 114 | client.simple_set("mykey", "Hello").unwrap(); 115 | 116 | assert!(client.pexpire("mykey", 1500).unwrap()); 117 | 118 | let rest = client.ttl("mykey").unwrap(); 119 | assert_eq!(rest, 1); 120 | 121 | // let rest = client.pttl("mykey").unwrap(); 122 | // assert_eq!(rest, 1499); 123 | 124 | client.flushall().unwrap(); 125 | } 126 | 127 | #[test] 128 | #[ignore] 129 | pub fn test_pttl() { 130 | let mut client = RedisClient::new().unwrap(); 131 | 132 | client.simple_set("mykey", "Hello").unwrap(); 133 | client.expire("mykey", 1).unwrap(); 134 | 135 | let res = client.pttl("mykey").unwrap(); 136 | assert_eq!(res, 999); 137 | 138 | client.flushall().unwrap(); 139 | } 140 | 141 | #[test] 142 | pub fn test_rename() { 143 | let mut client = RedisClient::new().unwrap(); 144 | 145 | client.simple_set("mykey", "Hello").unwrap(); 146 | client.rename("mykey", "myotherkey").unwrap(); 147 | 148 | let value: String = client.get("myotherkey").unwrap(); 149 | assert_eq!(value, "Hello".to_string()); 150 | 151 | client.flushall().unwrap(); 152 | } 153 | 154 | #[test] 155 | pub fn test_renamenx() { 156 | let mut client = RedisClient::new().unwrap(); 157 | 158 | client.simple_set("mykey", "Hello").unwrap(); 159 | client.simple_set("myotherkey", "World").unwrap(); 160 | 161 | assert!(!client.renamenx("mykey", "myotherkey").unwrap()); 162 | let value: String = client.get("myotherkey").unwrap(); 163 | 164 | assert_eq!(value, "World".to_string()); 165 | client.flushall().unwrap(); 166 | } 167 | 168 | #[test] 169 | pub fn test_touch() { 170 | let mut client = RedisClient::new().unwrap(); 171 | 172 | client.simple_set("key1", "Hello").unwrap(); 173 | client.simple_set("key2", "World").unwrap(); 174 | 175 | let amount = client.touch(vec!["key1", "key2"]).unwrap(); 176 | 177 | assert_eq!(amount, 2); 178 | client.flushall().unwrap(); 179 | } 180 | 181 | #[test] 182 | pub fn test_ttl() { 183 | let mut client = RedisClient::new().unwrap(); 184 | client.simple_set("mykey", "Hello").unwrap(); 185 | client.expire("mykey", 10).unwrap(); 186 | 187 | let rest = client.ttl("mykey").unwrap(); 188 | assert_eq!(rest, 10); 189 | client.flushall().unwrap(); 190 | } 191 | 192 | #[test] 193 | pub fn test_type() { 194 | let mut client = RedisClient::new().unwrap(); 195 | 196 | client.simple_set("key1", "value").unwrap(); 197 | // client.lpush("key2", "value"); 198 | // client.sadd("key3", "value"); 199 | 200 | assert_eq!(client.type_("key1").unwrap(), DataType::String); 201 | 202 | client.flushall().unwrap(); 203 | } 204 | 205 | #[test] 206 | pub fn test_unlink() { 207 | let mut client = RedisClient::new().unwrap(); 208 | 209 | client.simple_set("key1", "Hello").unwrap(); 210 | client.simple_set("key2", "World").unwrap(); 211 | 212 | let amount = client.unlink(vec!["key1", "key2", "key3"]).unwrap(); 213 | assert_eq!(amount, 2); 214 | 215 | client.flushall().unwrap(); 216 | } 217 | -------------------------------------------------------------------------------- /tests/test_lists_commands.rs: -------------------------------------------------------------------------------- 1 | use redisclient::client::ListBeforeOrAfter::Before; 2 | use redisclient::RedisClient; 3 | 4 | #[test] 5 | #[ignore] 6 | pub fn test_brpoplpush() { 7 | let mut client = RedisClient::new().unwrap(); 8 | client.flushall().unwrap(); 9 | } 10 | 11 | #[test] 12 | pub fn test_lindex() { 13 | let mut client = RedisClient::new().unwrap(); 14 | 15 | client.lpush("mylist", vec!["World"]).unwrap(); 16 | client.lpush("mylist", vec!["Hello"]).unwrap(); 17 | 18 | assert_eq!( 19 | client.lindex::<&'static str, String>("mylist", 0).unwrap(), 20 | "Hello".to_string() 21 | ); 22 | assert_eq!( 23 | client.lindex::<&'static str, String>("mylist", -1).unwrap(), 24 | "World".to_string() 25 | ); 26 | assert_eq!( 27 | client.lindex::<&'static str, String>("mylist", 3).unwrap(), 28 | "".to_string() 29 | ); 30 | 31 | client.flushall().unwrap(); 32 | } 33 | 34 | #[test] 35 | pub fn test_linsert() { 36 | let mut client = RedisClient::new().unwrap(); 37 | 38 | client.rpush("mylist", vec!["Hello"]).unwrap(); 39 | client.rpush("mylist", vec!["World"]).unwrap(); 40 | 41 | let pos = client.linsert("mylist", Before, "World", "There").unwrap(); 42 | assert_eq!(pos, 3); 43 | 44 | assert_eq!( 45 | client.lrange::<&'static str, String>("mylist", 0, -1).unwrap(), 46 | vec!["Hello".to_string(), "There".to_string(), "World".to_string()] 47 | ); 48 | 49 | client.flushall().unwrap(); 50 | } 51 | 52 | #[test] 53 | pub fn test_llen() { 54 | let mut client = RedisClient::new().unwrap(); 55 | 56 | client.lpush("mylist", vec!["World"]).unwrap(); 57 | client.lpush("mylist", vec!["Hello"]).unwrap(); 58 | 59 | assert_eq!(client.llen("mylist").unwrap(), 2); 60 | 61 | client.flushall().unwrap(); 62 | } 63 | 64 | #[test] 65 | pub fn test_lpop() { 66 | let mut client = RedisClient::new().unwrap(); 67 | 68 | client.rpush("mylist", vec!["one", "two", "three"]).unwrap(); 69 | let element: String = client.lpop("mylist").unwrap(); 70 | assert_eq!(element, "one".to_string()); 71 | 72 | let elements: Vec = client.lrange("mylist", 0, -1).unwrap(); 73 | assert_eq!(elements, vec!["two".to_string(), "three".to_string()]); 74 | 75 | client.flushall().unwrap(); 76 | } 77 | 78 | #[test] 79 | pub fn test_lpush() { 80 | let mut client = RedisClient::new().unwrap(); 81 | 82 | client.lpush("mylist", vec!["world", "hello"]).unwrap(); 83 | let elements: Vec = client.lrange("mylist", 0, -1).unwrap(); 84 | 85 | assert_eq!(elements, vec!["hello".to_string(), "world".to_string()]); 86 | 87 | client.flushall().unwrap(); 88 | } 89 | 90 | #[test] 91 | pub fn test_lpushx() { 92 | let mut client = RedisClient::new().unwrap(); 93 | 94 | client.lpush("mylist", vec!["World"]).unwrap(); 95 | 96 | assert_eq!(client.lpushx("mylist", vec!["Hello"]).unwrap(), 2); 97 | assert_eq!(client.lpushx("myotherlist", vec!["Hello"]).unwrap(), 0); 98 | 99 | assert_eq!( 100 | client.lrange::<&'static str, String>("mylist", 0, -1).unwrap(), 101 | vec!["Hello".to_string(), "World".to_string()] 102 | ); 103 | 104 | assert_eq!( 105 | client.lrange::<&'static str, String>("myotherlist", 0, -1).unwrap(), 106 | Vec::::new() 107 | ); 108 | 109 | client.flushall().unwrap(); 110 | } 111 | 112 | #[test] 113 | pub fn test_lrange() { 114 | let mut client = RedisClient::new().unwrap(); 115 | client.rpush("mylist", vec!["one", "two", "three"]).unwrap(); 116 | 117 | let elements: Vec = client.lrange("mylist", 0, 0).unwrap(); 118 | assert_eq!(elements, vec!["one".to_string()]); 119 | let elements: Vec = client.lrange("mylist", -3, 2).unwrap(); 120 | assert_eq!( 121 | elements, 122 | vec!["one".to_string(), "two".to_string(), "three".to_string()] 123 | ); 124 | client.flushall().unwrap(); 125 | } 126 | 127 | #[test] 128 | pub fn test_lrem() { 129 | let mut client = RedisClient::new().unwrap(); 130 | client.rpush("mylist", vec!["hello", "hello", "foo", "hello"]).unwrap(); 131 | 132 | let amount = client.lrem("mylist", -2, "hello").unwrap(); 133 | assert_eq!(amount, 2); 134 | 135 | let elements: Vec = client.lrange("mylist", 0, -1).unwrap(); 136 | assert_eq!(elements, vec!["hello".to_string(), "foo".to_string()]); 137 | 138 | client.flushall().unwrap(); 139 | } 140 | 141 | #[test] 142 | pub fn test_lset() { 143 | let mut client = RedisClient::new().unwrap(); 144 | client.rpush("mylist", vec!["onw", "two", "three"]).unwrap(); 145 | client.lset("mylist", 0, "four").unwrap(); 146 | client.lset("mylist", -2, "five").unwrap(); 147 | 148 | let elements: Vec = client.lrange("mylist", 0, -1).unwrap(); 149 | assert_eq!( 150 | elements, 151 | vec!["four".to_string(), "five".to_string(), "three".to_string()] 152 | ); 153 | 154 | client.flushall().unwrap(); 155 | } 156 | 157 | #[test] 158 | pub fn test_ltrim() { 159 | let mut client = RedisClient::new().unwrap(); 160 | 161 | client.rpush("mylist", vec!["one", "two", "three"]).unwrap(); 162 | client.ltrim("mylist", 1, -1).unwrap(); 163 | let elements: Vec = client.lrange("mylist", 0, -1).unwrap(); 164 | 165 | assert_eq!(elements, vec!["two".to_string(), "three".to_string()]); 166 | 167 | client.flushall().unwrap(); 168 | } 169 | 170 | #[test] 171 | pub fn test_rpop() { 172 | let mut client = RedisClient::new().unwrap(); 173 | 174 | client.rpush("mylist", vec!["one", "two", "three"]).unwrap(); 175 | let element: String = client.rpop("mylist").unwrap(); 176 | assert_eq!(element, "three".to_string()); 177 | 178 | let elements: Vec = client.lrange("mylist", 0, -1).unwrap(); 179 | assert_eq!(elements, vec!["one".to_string(), "two".to_string()]); 180 | 181 | client.flushall().unwrap(); 182 | } 183 | 184 | #[test] 185 | pub fn test_rpoplpush() { 186 | let mut client = RedisClient::new().unwrap(); 187 | 188 | client.rpush("mylist", vec!["one", "two", "three"]).unwrap(); 189 | let element: String = client.rpoplpush("mylist", "myotherlist").unwrap(); 190 | assert_eq!(element, "three".to_string()); 191 | 192 | let elements: Vec = client.lrange("mylist", 0, -1).unwrap(); 193 | assert_eq!(elements, vec!["one".to_string(), "two".to_string()]); 194 | 195 | let elements: Vec = client.lrange("myotherlist", 0, -1).unwrap(); 196 | assert_eq!(elements, vec!["three".to_string()]); 197 | 198 | client.flushall().unwrap(); 199 | } 200 | 201 | #[test] 202 | pub fn test_rpush() { 203 | let mut client = RedisClient::new().unwrap(); 204 | 205 | client.rpush("mylist", vec!["Hello", "World"]).unwrap(); 206 | let elements: Vec = client.lrange("mylist", 0, -1).unwrap(); 207 | 208 | assert_eq!(elements, vec!["Hello".to_string(), "World".to_string()]); 209 | 210 | client.flushall().unwrap(); 211 | } 212 | 213 | #[test] 214 | pub fn test_rpushx() { 215 | let mut client = RedisClient::new().unwrap(); 216 | 217 | client.rpush("mylist", vec!["Hello"]).unwrap(); 218 | client.rpushx("mylist", vec!["World"]).unwrap(); 219 | client.rpushx("myotherlist", vec!["World"]).unwrap(); 220 | 221 | let elements: Vec = client.lrange("mylist", 0, -1).unwrap(); 222 | assert_eq!(elements, vec!["Hello".to_string(), "World".to_string()]); 223 | 224 | let elements: Vec = client.lrange("myotherlist", 0, -1).unwrap(); 225 | assert!(elements.is_empty()); 226 | 227 | client.flushall().unwrap(); 228 | } 229 | -------------------------------------------------------------------------------- /tests/test_protocol.rs: -------------------------------------------------------------------------------- 1 | use redisclient::connection::Reply; 2 | use redisclient::protocol::{RedisDeserializationProtocol, RedisSerializationProtocol}; 3 | 4 | // #[test] 5 | // pub fn test_vector_serialization() { 6 | // let data = b"Hello world".to_vec(); 7 | // 8 | // let got = data.serialization(); 9 | // 10 | // let expected = Vec::from("$11\r\nHello world\r\n"); 11 | // assert_eq!(expected, got); 12 | // } 13 | 14 | #[test] 15 | pub fn test_string_serialization() { 16 | let s = String::from("Hello world"); 17 | 18 | let got = s.serialization(); 19 | 20 | let expected = vec![ 21 | 36, 49, 49, 13, 10, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 13, 10, 22 | ]; 23 | assert_eq!(expected, got); 24 | } 25 | 26 | #[test] 27 | pub fn test_u64_serialization() { 28 | let num: u64 = 132; 29 | 30 | let got = num.serialization(); 31 | 32 | let expected = Vec::from("$3\r\n132\r\n"); 33 | assert_eq!(expected, got); 34 | } 35 | 36 | #[test] 37 | pub fn test_u64_deserialization() { 38 | let reply = Reply::Integers(vec![54, 48]); 39 | 40 | let got = ::deserialization(reply).unwrap(); 41 | 42 | let expected = 60_u64; 43 | assert_eq!(expected, got); 44 | } 45 | 46 | #[test] 47 | pub fn test_i64_serialization() { 48 | let num: i64 = -321; 49 | 50 | let got = num.serialization(); 51 | 52 | let expected = Vec::from("$4\r\n-321\r\n"); 53 | assert_eq!(expected, got); 54 | } 55 | 56 | #[test] 57 | pub fn test_f32_serialization() { 58 | let fnum: f32 = -1.23; 59 | 60 | let got = fnum.serialization(); 61 | 62 | let expected = Vec::from("$5\r\n-1.23\r\n"); 63 | assert_eq!(expected, got); 64 | } 65 | 66 | #[test] 67 | pub fn test_f64_serialization() { 68 | let fnum: f64 = -1.23; 69 | 70 | let got = fnum.serialization(); 71 | 72 | let expected = Vec::from("$5\r\n-1.23\r\n"); 73 | assert_eq!(expected, got); 74 | } 75 | -------------------------------------------------------------------------------- /tests/test_sets_commands.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use redisclient::hash_set; 4 | use redisclient::RedisClient; 5 | 6 | #[test] 7 | pub fn test_sadd() { 8 | let mut client = RedisClient::new().unwrap(); 9 | 10 | client.sadd("myset", hash_set!["Hello", "World", "World"]).unwrap(); 11 | let members: HashSet = client.smembers("myset").unwrap(); 12 | assert_eq!(members, hash_set!("Hello".to_string(), "World".to_string())); 13 | 14 | client.flushall().unwrap(); 15 | } 16 | 17 | #[test] 18 | pub fn test_scard() { 19 | let mut client = RedisClient::new().unwrap(); 20 | 21 | client.sadd("myset", hash_set!["Hello", "World"]).unwrap(); 22 | let amount = client.scard("myset").unwrap(); 23 | assert_eq!(amount, 2); 24 | 25 | client.flushall().unwrap(); 26 | } 27 | 28 | #[test] 29 | pub fn test_sdiff() { 30 | let mut client = RedisClient::new().unwrap(); 31 | 32 | client.sadd("key1", hash_set!["a", "b", "c"]).unwrap(); 33 | client.sadd("key2", hash_set!["c", "d", "e"]).unwrap(); 34 | let diff: HashSet = client.sdiff(vec!["key1", "key2"]).unwrap(); 35 | assert_eq!(diff, hash_set!("a".to_string(), "b".to_string())); 36 | 37 | client.flushall().unwrap(); 38 | } 39 | 40 | #[test] 41 | pub fn test_sdiffstore() { 42 | let mut client = RedisClient::new().unwrap(); 43 | 44 | client.sadd("key1", hash_set!["a", "b", "c"]).unwrap(); 45 | client.sadd("key2", hash_set!["c", "d", "e"]).unwrap(); 46 | client.sdiffstore("key", vec!["key1", "key2"]).unwrap(); 47 | let elements: HashSet = client.smembers("key").unwrap(); 48 | 49 | assert_eq!(elements, hash_set!("a".to_string(), "b".to_string())); 50 | 51 | client.flushall().unwrap(); 52 | } 53 | 54 | #[test] 55 | pub fn test_sinter() { 56 | let mut client = RedisClient::new().unwrap(); 57 | 58 | client.sadd("key1", hash_set!("a", "b", "c")).unwrap(); 59 | client.sadd("key2", hash_set!("c", "d", "e")).unwrap(); 60 | let members: HashSet = client.sinter(vec!["key1", "key2"]).unwrap(); 61 | 62 | assert_eq!(members, hash_set!("c".to_string())); 63 | 64 | client.flushall().unwrap(); 65 | } 66 | 67 | #[test] 68 | pub fn test_sinterstore() { 69 | let mut client = RedisClient::new().unwrap(); 70 | 71 | client.sadd("key1", hash_set!("a", "b", "c")).unwrap(); 72 | client.sadd("key2", hash_set!("c", "d", "e")).unwrap(); 73 | 74 | let amount = client.sinterstore("key", vec!["key1", "key2"]).unwrap(); 75 | assert_eq!(amount, 1); 76 | let elements: HashSet = client.smembers("key").unwrap(); 77 | assert_eq!(elements, hash_set!("c".to_string())); 78 | 79 | client.flushall().unwrap(); 80 | } 81 | 82 | #[test] 83 | pub fn test_sismember() { 84 | let mut client = RedisClient::new().unwrap(); 85 | 86 | client.sadd("myset", hash_set!["one"]).unwrap(); 87 | assert!(client.sismember("myset", "one".to_string()).unwrap()); 88 | assert!(!client.sismember("myset", "two".to_string()).unwrap()); 89 | 90 | client.flushall().unwrap(); 91 | } 92 | 93 | #[test] 94 | #[ignore] 95 | pub fn test_smismember() { 96 | let mut client = RedisClient::new().unwrap(); 97 | 98 | client.sadd("myset", hash_set!("one")).unwrap(); 99 | assert_eq!( 100 | client 101 | .smismember("myset", hash_set!("one".to_string(), "notamember".to_string())) 102 | .unwrap(), 103 | vec![true, false] 104 | ); 105 | 106 | client.flushall().unwrap(); 107 | } 108 | 109 | #[test] 110 | pub fn test_smove() { 111 | let mut client = RedisClient::new().unwrap(); 112 | 113 | client.sadd("myset", hash_set!("one", "two")).unwrap(); 114 | client.sadd("myotherset", hash_set!("three")).unwrap(); 115 | 116 | client.smove("myset", "myotherset", "two").unwrap(); 117 | 118 | let members: HashSet = client.smembers("myset").unwrap(); 119 | assert_eq!(members, hash_set!("one".to_string())); 120 | 121 | let members: HashSet = client.smembers("myotherset").unwrap(); 122 | assert_eq!(members, hash_set!("two".to_string(), "three".to_string())); 123 | 124 | client.flushall().unwrap(); 125 | } 126 | 127 | #[test] 128 | pub fn test_spop() { 129 | let mut client = RedisClient::new().unwrap(); 130 | 131 | client.sadd("myset", hash_set!("one", "two", "three")).unwrap(); 132 | let elements: HashSet = client.spop("myset", Some(3)).unwrap(); 133 | assert_eq!( 134 | elements, 135 | hash_set!("one".to_string(), "two".to_string(), "three".to_string()) 136 | ); 137 | 138 | client.flushall().unwrap(); 139 | } 140 | 141 | #[test] 142 | #[ignore] 143 | pub fn test_srandmember() { 144 | let mut client = RedisClient::new().unwrap(); 145 | client.flushall().unwrap(); 146 | } 147 | 148 | #[test] 149 | pub fn test_srem() { 150 | let mut client = RedisClient::new().unwrap(); 151 | 152 | client.sadd("myset", hash_set!("one", "two", "three")).unwrap(); 153 | assert_eq!(client.srem("myset", hash_set!("one")).unwrap(), 1); 154 | assert_eq!(client.srem("myset", hash_set!("four")).unwrap(), 0); 155 | 156 | let rest_members: HashSet = client.smembers("myset").unwrap(); 157 | assert_eq!(rest_members, hash_set!("two".to_string(), "three".to_string())); 158 | 159 | client.flushall().unwrap(); 160 | } 161 | 162 | #[test] 163 | pub fn test_sunion() { 164 | let mut client = RedisClient::new().unwrap(); 165 | 166 | client.sadd("key1", hash_set!("a", "b")).unwrap(); 167 | client.sadd("key2", hash_set!("c", "d", "e")).unwrap(); 168 | 169 | let members: HashSet = client.sunion(vec!["key1", "key2"]).unwrap(); 170 | assert_eq!( 171 | members, 172 | hash_set!( 173 | "a".to_string(), 174 | "b".to_string(), 175 | "c".to_string(), 176 | "d".to_string(), 177 | "e".to_string() 178 | ) 179 | ); 180 | 181 | client.flushall().unwrap(); 182 | } 183 | 184 | #[test] 185 | pub fn test_sunionstore() { 186 | let mut client = RedisClient::new().unwrap(); 187 | 188 | client.sadd("key1", hash_set!("a", "b", "c")).unwrap(); 189 | client.sadd("key2", hash_set!("c", "d", "e")).unwrap(); 190 | 191 | let amount = client.sunionstore("key", vec!["key1", "key2"]).unwrap(); 192 | assert_eq!(amount, 5); 193 | let members: HashSet = client.smembers("key").unwrap(); 194 | assert_eq!( 195 | members, 196 | hash_set!( 197 | "a".to_string(), 198 | "b".to_string(), 199 | "c".to_string(), 200 | "d".to_string(), 201 | "e".to_string() 202 | ) 203 | ); 204 | 205 | client.flushall().unwrap(); 206 | } 207 | -------------------------------------------------------------------------------- /tests/test_sorted_sets.rs: -------------------------------------------------------------------------------- 1 | use redisclient::RedisClient; 2 | 3 | pub fn test_zadd() { 4 | let mut client = RedisClient::new().unwrap(); 5 | 6 | assert_eq!(client.zadd("myzset", vec![(1, "one")]).unwrap(), 1); 7 | assert_eq!(client.zadd("myzset", vec![(1, "uno")]).unwrap(), 1); 8 | assert_eq!(client.zadd("myzset", vec![(2, "two"), (3, "three")]).unwrap(), 2); 9 | 10 | // let _ = client.zrange("myzset", 0, -1, "WITHSCORES"); 11 | 12 | client.flushall().unwrap(); 13 | } 14 | 15 | #[test] 16 | pub fn test_zcard() { 17 | let mut client = RedisClient::new().unwrap(); 18 | 19 | assert_eq!(client.zadd("myzset", vec![(1, "one"), (2, "two")]).unwrap(), 2); 20 | assert_eq!(client.zcard("myzset").unwrap(), 2); 21 | 22 | client.flushall().unwrap(); 23 | } 24 | 25 | #[test] 26 | pub fn test_zcount() { 27 | let mut client = RedisClient::new().unwrap(); 28 | 29 | assert_eq!( 30 | client 31 | .zadd("myzset", vec![(1, "one"), (2, "two"), (3, "three")]) 32 | .unwrap(), 33 | 3 34 | ); 35 | 36 | assert_eq!( 37 | client.zcount("myzset", isize::min_value(), isize::max_value()).unwrap(), 38 | 3 39 | ); 40 | assert_eq!(client.zcount("myzset", 2, 3).unwrap(), 2); 41 | 42 | client.flushall().unwrap(); 43 | } 44 | --------------------------------------------------------------------------------