1use crate::{DbError, RedisConfig, RedisValue, Result};
2use deadpool_redis::{Config, Pool, Runtime};
3
4#[derive(Clone)]
8pub struct RedisClient {
9 pool: Pool,
11 #[allow(dead_code)]
13 config: RedisConfig,
14}
15
16impl RedisClient {
17 pub async fn connect(url: impl Into<String>) -> Result<Self> {
37 let config = RedisConfig::default();
38 Self::connect_with_config(url, config).await
39 }
40
41 pub async fn connect_with_config(url: impl Into<String>, config: RedisConfig) -> Result<Self> {
63 let url = url.into();
64
65 let pool_config = Config {
67 url: Some(url.clone()),
68 ..Default::default()
69 };
70
71 let pool = pool_config
73 .create_pool(Some(Runtime::Tokio1))
74 .map_err(|e| DbError::RedisConnectionError(format!("创建连接池失败: {}", e)))?;
75
76 let mut conn = pool
78 .get()
79 .await
80 .map_err(|e| DbError::RedisConnectionError(format!("获取连接失败: {}", e)))?;
81
82 redis::cmd("PING")
84 .query_async::<String>(&mut *conn)
85 .await
86 .map_err(|e| DbError::RedisConnectionError(format!("连接测试失败: {}", e)))?;
87
88 if config.enable_logging {
89 log::info!("Redis 连接成功: {}", url);
90 }
91
92 Ok(Self { pool, config })
93 }
94
95 pub fn pool(&self) -> &Pool {
100 &self.pool
101 }
102
103 pub async fn execute(&self, cmd: &redis::Cmd) -> Result<RedisValue> {
129 let mut conn = self
130 .pool
131 .get()
132 .await
133 .map_err(|e| DbError::RedisPoolError(format!("获取连接失败: {}", e)))?;
134
135 let value: redis::Value = cmd
136 .query_async(&mut *conn)
137 .await
138 .map_err(|e| DbError::RedisCommandError(format!("命令执行失败: {}", e)))?;
139
140 Ok(RedisValue::from(value))
141 }
142
143 pub async fn set(&self, key: impl Into<String>, value: impl Into<String>) -> Result<()> {
155 let mut cmd = redis::cmd("SET");
156 cmd.arg(key.into()).arg(value.into());
157 self.execute(&cmd).await?;
158 Ok(())
159 }
160
161 pub async fn get(&self, key: impl Into<String>) -> Result<Option<String>> {
171 let mut cmd = redis::cmd("GET");
172 cmd.arg(key.into());
173 let value = self.execute(&cmd).await?;
174 Ok(value.as_string())
175 }
176
177 pub async fn setex(
184 &self,
185 key: impl Into<String>,
186 seconds: i64,
187 value: impl Into<String>,
188 ) -> Result<()> {
189 let mut cmd = redis::cmd("SETEX");
190 cmd.arg(key.into()).arg(seconds).arg(value.into());
191 self.execute(&cmd).await?;
192 Ok(())
193 }
194
195 pub async fn setnx(&self, key: impl Into<String>, value: impl Into<String>) -> Result<bool> {
201 let mut cmd = redis::cmd("SETNX");
202 cmd.arg(key.into()).arg(value.into());
203 let result = self.execute(&cmd).await?;
204 Ok(result.as_i64() == Some(1))
205 }
206
207 pub async fn getset(
213 &self,
214 key: impl Into<String>,
215 value: impl Into<String>,
216 ) -> Result<Option<String>> {
217 let mut cmd = redis::cmd("GETSET");
218 cmd.arg(key.into()).arg(value.into());
219 let result = self.execute(&cmd).await?;
220 Ok(result.as_string())
221 }
222
223 pub async fn mget(&self, keys: &[String]) -> Result<Vec<Option<String>>> {
228 let mut cmd = redis::cmd("MGET");
229 for key in keys {
230 cmd.arg(key);
231 }
232 let result = self.execute(&cmd).await?;
233 if let Some(arr) = result.as_array() {
234 Ok(arr.iter().map(|v| v.as_string()).collect())
235 } else {
236 Ok(vec![])
237 }
238 }
239
240 pub async fn mset(&self, pairs: &[(String, String)]) -> Result<()> {
245 let mut cmd = redis::cmd("MSET");
246 for (key, value) in pairs {
247 cmd.arg(key).arg(value);
248 }
249 self.execute(&cmd).await?;
250 Ok(())
251 }
252
253 pub async fn incr(&self, key: impl Into<String>) -> Result<i64> {
258 let mut cmd = redis::cmd("INCR");
259 cmd.arg(key.into());
260 let result = self.execute(&cmd).await?;
261 result
262 .as_i64()
263 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
264 }
265
266 pub async fn incrby(&self, key: impl Into<String>, increment: i64) -> Result<i64> {
271 let mut cmd = redis::cmd("INCRBY");
272 cmd.arg(key.into()).arg(increment);
273 let result = self.execute(&cmd).await?;
274 result
275 .as_i64()
276 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
277 }
278
279 pub async fn decr(&self, key: impl Into<String>) -> Result<i64> {
284 let mut cmd = redis::cmd("DECR");
285 cmd.arg(key.into());
286 let result = self.execute(&cmd).await?;
287 result
288 .as_i64()
289 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
290 }
291
292 pub async fn decrby(&self, key: impl Into<String>, decrement: i64) -> Result<i64> {
297 let mut cmd = redis::cmd("DECRBY");
298 cmd.arg(key.into()).arg(decrement);
299 let result = self.execute(&cmd).await?;
300 result
301 .as_i64()
302 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
303 }
304
305 pub async fn append(&self, key: impl Into<String>, value: impl Into<String>) -> Result<i64> {
310 let mut cmd = redis::cmd("APPEND");
311 cmd.arg(key.into()).arg(value.into());
312 let result = self.execute(&cmd).await?;
313 result
314 .as_i64()
315 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
316 }
317
318 pub async fn strlen(&self, key: impl Into<String>) -> Result<i64> {
323 let mut cmd = redis::cmd("STRLEN");
324 cmd.arg(key.into());
325 let result = self.execute(&cmd).await?;
326 result
327 .as_i64()
328 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
329 }
330
331 pub async fn hset(
339 &self,
340 key: impl Into<String>,
341 field: impl Into<String>,
342 value: impl Into<String>,
343 ) -> Result<bool> {
344 let mut cmd = redis::cmd("HSET");
345 cmd.arg(key.into()).arg(field.into()).arg(value.into());
346 let result = self.execute(&cmd).await?;
347 Ok(result.as_i64() == Some(1))
348 }
349
350 pub async fn hget(
356 &self,
357 key: impl Into<String>,
358 field: impl Into<String>,
359 ) -> Result<Option<String>> {
360 let mut cmd = redis::cmd("HGET");
361 cmd.arg(key.into()).arg(field.into());
362 let result = self.execute(&cmd).await?;
363 Ok(result.as_string())
364 }
365
366 pub async fn hdel(&self, key: impl Into<String>, fields: &[String]) -> Result<i64> {
371 let mut cmd = redis::cmd("HDEL");
372 cmd.arg(key.into());
373 for field in fields {
374 cmd.arg(field);
375 }
376 let result = self.execute(&cmd).await?;
377 result
378 .as_i64()
379 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
380 }
381
382 pub async fn hexists(&self, key: impl Into<String>, field: impl Into<String>) -> Result<bool> {
388 let mut cmd = redis::cmd("HEXISTS");
389 cmd.arg(key.into()).arg(field.into());
390 let result = self.execute(&cmd).await?;
391 Ok(result.as_i64() == Some(1))
392 }
393
394 pub async fn hmset(&self, key: impl Into<String>, fields: &[(String, String)]) -> Result<()> {
396 let mut cmd = redis::cmd("HMSET");
397 cmd.arg(key.into());
398 for (field, value) in fields {
399 cmd.arg(field).arg(value);
400 }
401 self.execute(&cmd).await?;
402 Ok(())
403 }
404
405 pub async fn hmget(
410 &self,
411 key: impl Into<String>,
412 fields: &[String],
413 ) -> Result<Vec<Option<String>>> {
414 let mut cmd = redis::cmd("HMGET");
415 cmd.arg(key.into());
416 for field in fields {
417 cmd.arg(field);
418 }
419 let result = self.execute(&cmd).await?;
420 if let Some(arr) = result.as_array() {
421 Ok(arr.iter().map(|v| v.as_string()).collect())
422 } else {
423 Ok(vec![])
424 }
425 }
426
427 pub async fn hgetall(&self, key: impl Into<String>) -> Result<Vec<(String, String)>> {
432 let mut cmd = redis::cmd("HGETALL");
433 cmd.arg(key.into());
434 let result = self.execute(&cmd).await?;
435 if let Some(arr) = result.as_array() {
436 let mut pairs = Vec::new();
437 for i in (0..arr.len()).step_by(2) {
438 if i + 1 < arr.len()
439 && let (Some(field), Some(value)) = (arr[i].as_string(), arr[i + 1].as_string())
440 {
441 pairs.push((field, value));
442 }
443 }
444 Ok(pairs)
445 } else {
446 Ok(vec![])
447 }
448 }
449
450 pub async fn hlen(&self, key: impl Into<String>) -> Result<i64> {
452 let mut cmd = redis::cmd("HLEN");
453 cmd.arg(key.into());
454 let result = self.execute(&cmd).await?;
455 result
456 .as_i64()
457 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
458 }
459
460 pub async fn hkeys(&self, key: impl Into<String>) -> Result<Vec<String>> {
462 let mut cmd = redis::cmd("HKEYS");
463 cmd.arg(key.into());
464 let result = self.execute(&cmd).await?;
465 if let Some(arr) = result.as_array() {
466 Ok(arr.iter().filter_map(|v| v.as_string()).collect())
467 } else {
468 Ok(vec![])
469 }
470 }
471
472 pub async fn hvals(&self, key: impl Into<String>) -> Result<Vec<String>> {
474 let mut cmd = redis::cmd("HVALS");
475 cmd.arg(key.into());
476 let result = self.execute(&cmd).await?;
477 if let Some(arr) = result.as_array() {
478 Ok(arr.iter().filter_map(|v| v.as_string()).collect())
479 } else {
480 Ok(vec![])
481 }
482 }
483
484 pub async fn hincrby(
486 &self,
487 key: impl Into<String>,
488 field: impl Into<String>,
489 increment: i64,
490 ) -> Result<i64> {
491 let mut cmd = redis::cmd("HINCRBY");
492 cmd.arg(key.into()).arg(field.into()).arg(increment);
493 let result = self.execute(&cmd).await?;
494 result
495 .as_i64()
496 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
497 }
498
499 pub async fn hincrbyfloat(
501 &self,
502 key: impl Into<String>,
503 field: impl Into<String>,
504 increment: f64,
505 ) -> Result<f64> {
506 let mut cmd = redis::cmd("HINCRBYFLOAT");
507 cmd.arg(key.into()).arg(field.into()).arg(increment);
508 let result = self.execute(&cmd).await?;
509 if let Some(s) = result.as_string() {
511 s.parse::<f64>()
512 .map_err(|_| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
513 } else {
514 result
515 .as_f64()
516 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
517 }
518 }
519
520 pub async fn lpush(&self, key: impl Into<String>, values: &[String]) -> Result<i64> {
527 let mut cmd = redis::cmd("LPUSH");
528 cmd.arg(key.into());
529 for value in values {
530 cmd.arg(value);
531 }
532 let result = self.execute(&cmd).await?;
533 result
534 .as_i64()
535 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
536 }
537
538 pub async fn rpush(&self, key: impl Into<String>, values: &[String]) -> Result<i64> {
543 let mut cmd = redis::cmd("RPUSH");
544 cmd.arg(key.into());
545 for value in values {
546 cmd.arg(value);
547 }
548 let result = self.execute(&cmd).await?;
549 result
550 .as_i64()
551 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
552 }
553
554 pub async fn lpop(&self, key: impl Into<String>) -> Result<Option<String>> {
560 let mut cmd = redis::cmd("LPOP");
561 cmd.arg(key.into());
562 let result = self.execute(&cmd).await?;
563 Ok(result.as_string())
564 }
565
566 pub async fn rpop(&self, key: impl Into<String>) -> Result<Option<String>> {
572 let mut cmd = redis::cmd("RPOP");
573 cmd.arg(key.into());
574 let result = self.execute(&cmd).await?;
575 Ok(result.as_string())
576 }
577
578 pub async fn lrange(
584 &self,
585 key: impl Into<String>,
586 start: i64,
587 stop: i64,
588 ) -> Result<Vec<String>> {
589 let mut cmd = redis::cmd("LRANGE");
590 cmd.arg(key.into()).arg(start).arg(stop);
591 let result = self.execute(&cmd).await?;
592 if let Some(arr) = result.as_array() {
593 Ok(arr.iter().filter_map(|v| v.as_string()).collect())
594 } else {
595 Ok(vec![])
596 }
597 }
598
599 pub async fn llen(&self, key: impl Into<String>) -> Result<i64> {
601 let mut cmd = redis::cmd("LLEN");
602 cmd.arg(key.into());
603 let result = self.execute(&cmd).await?;
604 result
605 .as_i64()
606 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
607 }
608
609 pub async fn lindex(&self, key: impl Into<String>, index: i64) -> Result<Option<String>> {
615 let mut cmd = redis::cmd("LINDEX");
616 cmd.arg(key.into()).arg(index);
617 let result = self.execute(&cmd).await?;
618 Ok(result.as_string())
619 }
620
621 pub async fn lset(
623 &self,
624 key: impl Into<String>,
625 index: i64,
626 value: impl Into<String>,
627 ) -> Result<()> {
628 let mut cmd = redis::cmd("LSET");
629 cmd.arg(key.into()).arg(index).arg(value.into());
630 self.execute(&cmd).await?;
631 Ok(())
632 }
633
634 pub async fn ltrim(&self, key: impl Into<String>, start: i64, stop: i64) -> Result<()> {
636 let mut cmd = redis::cmd("LTRIM");
637 cmd.arg(key.into()).arg(start).arg(stop);
638 self.execute(&cmd).await?;
639 Ok(())
640 }
641
642 pub async fn sadd(&self, key: impl Into<String>, members: &[String]) -> Result<i64> {
649 let mut cmd = redis::cmd("SADD");
650 cmd.arg(key.into());
651 for member in members {
652 cmd.arg(member);
653 }
654 let result = self.execute(&cmd).await?;
655 result
656 .as_i64()
657 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
658 }
659
660 pub async fn srem(&self, key: impl Into<String>, members: &[String]) -> Result<i64> {
665 let mut cmd = redis::cmd("SREM");
666 cmd.arg(key.into());
667 for member in members {
668 cmd.arg(member);
669 }
670 let result = self.execute(&cmd).await?;
671 result
672 .as_i64()
673 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
674 }
675
676 pub async fn smembers(&self, key: impl Into<String>) -> Result<Vec<String>> {
678 let mut cmd = redis::cmd("SMEMBERS");
679 cmd.arg(key.into());
680 let result = self.execute(&cmd).await?;
681 if let Some(arr) = result.as_array() {
682 Ok(arr.iter().filter_map(|v| v.as_string()).collect())
683 } else {
684 Ok(vec![])
685 }
686 }
687
688 pub async fn sismember(
694 &self,
695 key: impl Into<String>,
696 member: impl Into<String>,
697 ) -> Result<bool> {
698 let mut cmd = redis::cmd("SISMEMBER");
699 cmd.arg(key.into()).arg(member.into());
700 let result = self.execute(&cmd).await?;
701 Ok(result.as_i64() == Some(1))
702 }
703
704 pub async fn scard(&self, key: impl Into<String>) -> Result<i64> {
706 let mut cmd = redis::cmd("SCARD");
707 cmd.arg(key.into());
708 let result = self.execute(&cmd).await?;
709 result
710 .as_i64()
711 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
712 }
713
714 pub async fn spop(&self, key: impl Into<String>) -> Result<Option<String>> {
720 let mut cmd = redis::cmd("SPOP");
721 cmd.arg(key.into());
722 let result = self.execute(&cmd).await?;
723 Ok(result.as_string())
724 }
725
726 pub async fn srandmember(&self, key: impl Into<String>) -> Result<Option<String>> {
732 let mut cmd = redis::cmd("SRANDMEMBER");
733 cmd.arg(key.into());
734 let result = self.execute(&cmd).await?;
735 Ok(result.as_string())
736 }
737
738 pub async fn zadd(&self, key: impl Into<String>, members: &[(f64, String)]) -> Result<i64> {
745 let mut cmd = redis::cmd("ZADD");
746 cmd.arg(key.into());
747 for (score, member) in members {
748 cmd.arg(score).arg(member);
749 }
750 let result = self.execute(&cmd).await?;
751 result
752 .as_i64()
753 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
754 }
755
756 pub async fn zrem(&self, key: impl Into<String>, members: &[String]) -> Result<i64> {
761 let mut cmd = redis::cmd("ZREM");
762 cmd.arg(key.into());
763 for member in members {
764 cmd.arg(member);
765 }
766 let result = self.execute(&cmd).await?;
767 result
768 .as_i64()
769 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
770 }
771
772 pub async fn zscore(
778 &self,
779 key: impl Into<String>,
780 member: impl Into<String>,
781 ) -> Result<Option<f64>> {
782 let mut cmd = redis::cmd("ZSCORE");
783 cmd.arg(key.into()).arg(member.into());
784 let result = self.execute(&cmd).await?;
785 if let Some(s) = result.as_string() {
786 Ok(s.parse::<f64>().ok())
787 } else {
788 Ok(result.as_f64())
789 }
790 }
791
792 pub async fn zcard(&self, key: impl Into<String>) -> Result<i64> {
794 let mut cmd = redis::cmd("ZCARD");
795 cmd.arg(key.into());
796 let result = self.execute(&cmd).await?;
797 result
798 .as_i64()
799 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
800 }
801
802 pub async fn zrange(
808 &self,
809 key: impl Into<String>,
810 start: i64,
811 stop: i64,
812 ) -> Result<Vec<String>> {
813 let mut cmd = redis::cmd("ZRANGE");
814 cmd.arg(key.into()).arg(start).arg(stop);
815 let result = self.execute(&cmd).await?;
816 if let Some(arr) = result.as_array() {
817 Ok(arr.iter().filter_map(|v| v.as_string()).collect())
818 } else {
819 Ok(vec![])
820 }
821 }
822
823 pub async fn zrangebyscore(
829 &self,
830 key: impl Into<String>,
831 min: f64,
832 max: f64,
833 ) -> Result<Vec<String>> {
834 let mut cmd = redis::cmd("ZRANGEBYSCORE");
835 cmd.arg(key.into()).arg(min).arg(max);
836 let result = self.execute(&cmd).await?;
837 if let Some(arr) = result.as_array() {
838 Ok(arr.iter().filter_map(|v| v.as_string()).collect())
839 } else {
840 Ok(vec![])
841 }
842 }
843
844 pub async fn zcount(&self, key: impl Into<String>, min: f64, max: f64) -> Result<i64> {
846 let mut cmd = redis::cmd("ZCOUNT");
847 cmd.arg(key.into()).arg(min).arg(max);
848 let result = self.execute(&cmd).await?;
849 result
850 .as_i64()
851 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
852 }
853
854 pub async fn zincrby(
859 &self,
860 key: impl Into<String>,
861 increment: f64,
862 member: impl Into<String>,
863 ) -> Result<f64> {
864 let mut cmd = redis::cmd("ZINCRBY");
865 cmd.arg(key.into()).arg(increment).arg(member.into());
866 let result = self.execute(&cmd).await?;
867 if let Some(s) = result.as_string() {
868 s.parse::<f64>()
869 .map_err(|_| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
870 } else {
871 result
872 .as_f64()
873 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
874 }
875 }
876
877 pub async fn del(&self, keys: &[String]) -> Result<i64> {
884 let mut cmd = redis::cmd("DEL");
885 for key in keys {
886 cmd.arg(key);
887 }
888 let result = self.execute(&cmd).await?;
889 result
890 .as_i64()
891 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
892 }
893
894 pub async fn exists(&self, keys: &[String]) -> Result<i64> {
899 let mut cmd = redis::cmd("EXISTS");
900 for key in keys {
901 cmd.arg(key);
902 }
903 let result = self.execute(&cmd).await?;
904 result
905 .as_i64()
906 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
907 }
908
909 pub async fn expire(&self, key: impl Into<String>, seconds: i64) -> Result<bool> {
915 let mut cmd = redis::cmd("EXPIRE");
916 cmd.arg(key.into()).arg(seconds);
917 let result = self.execute(&cmd).await?;
918 Ok(result.as_i64() == Some(1))
919 }
920
921 pub async fn ttl(&self, key: impl Into<String>) -> Result<i64> {
928 let mut cmd = redis::cmd("TTL");
929 cmd.arg(key.into());
930 let result = self.execute(&cmd).await?;
931 result
932 .as_i64()
933 .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
934 }
935
936 pub async fn persist(&self, key: impl Into<String>) -> Result<bool> {
942 let mut cmd = redis::cmd("PERSIST");
943 cmd.arg(key.into());
944 let result = self.execute(&cmd).await?;
945 Ok(result.as_i64() == Some(1))
946 }
947
948 pub async fn keys(&self, pattern: impl Into<String>) -> Result<Vec<String>> {
956 let mut cmd = redis::cmd("KEYS");
957 cmd.arg(pattern.into());
958 let result = self.execute(&cmd).await?;
959 if let Some(arr) = result.as_array() {
960 Ok(arr.iter().filter_map(|v| v.as_string()).collect())
961 } else {
962 Ok(vec![])
963 }
964 }
965}
966
967#[cfg(test)]
968mod tests {
969 use super::*;
970
971 #[test]
972 fn test_redis_client_clone() {
973 let config = RedisConfig::default();
975 let _ = config.clone();
977 }
978
979 #[test]
980 fn test_redis_config_default() {
981 let config = RedisConfig::default();
982 assert_eq!(config.max_connections, 10);
983 assert_eq!(config.connect_timeout, 5);
984 assert_eq!(config.wait_timeout, 10);
985 assert!(!config.enable_logging);
986 }
987
988 #[test]
989 fn test_redis_config_custom() {
990 let config = RedisConfig::new(20, 10, 15, true);
991 assert_eq!(config.max_connections, 20);
992 assert_eq!(config.connect_timeout, 10);
993 assert_eq!(config.wait_timeout, 15);
994 assert!(config.enable_logging);
995 }
996}