Skip to main content

yang_db/redis/
client.rs

1use crate::redis::{RedisPipeline, RedisTransaction};
2use crate::{DbError, RedisConfig, RedisValue, Result};
3use deadpool_redis::{Config, Pool, PoolConfig, Runtime, Timeouts};
4use std::time::Duration;
5
6/// 连接池状态信息
7#[derive(Debug, Clone)]
8pub struct PoolStatus {
9    /// 连接池最大连接数
10    pub max_size: usize,
11    /// 当前连接总数
12    pub size: usize,
13    /// 当前可用(空闲)连接数
14    pub available: usize,
15    /// 正在等待获取连接的请求数
16    pub waiting: usize,
17}
18
19/// Redis 客户端
20///
21/// 提供 Redis 数据库操作的统一接口,支持连接池管理
22#[derive(Clone)]
23pub struct RedisClient {
24    /// 连接池
25    pool: Pool,
26    /// 配置
27    #[allow(dead_code)]
28    config: RedisConfig,
29}
30
31impl RedisClient {
32    /// 连接到 Redis 服务器
33    ///
34    /// # 参数
35    /// - `url`: Redis 连接 URL,格式为 `redis://host:port` 或 `redis://host:port/db`
36    ///
37    /// # 返回
38    /// - `Ok(RedisClient)`: 连接成功
39    /// - `Err(DbError)`: 连接失败
40    ///
41    /// # 示例
42    /// ```no_run
43    /// use yang_db::RedisClient;
44    ///
45    /// #[tokio::main]
46    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
47    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
48    ///     Ok(())
49    /// }
50    /// ```
51    pub async fn connect(url: impl Into<String>) -> Result<Self> {
52        let config = RedisConfig::default();
53        Self::connect_with_config(url, config).await
54    }
55
56    /// 使用自定义配置连接到 Redis 服务器
57    ///
58    /// # 参数
59    /// - `url`: Redis 连接 URL
60    /// - `config`: Redis 配置
61    ///
62    /// # 返回
63    /// - `Ok(RedisClient)`: 连接成功
64    /// - `Err(DbError)`: 连接失败
65    ///
66    /// # 示例
67    /// ```no_run
68    /// use yang_db::{RedisClient, RedisConfig};
69    ///
70    /// #[tokio::main]
71    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
72    ///     let config = RedisConfig::new(20, 10, 15, true);
73    ///     let client = RedisClient::connect_with_config("redis://127.0.0.1:6379", config).await?;
74    ///     Ok(())
75    /// }
76    /// ```
77    pub async fn connect_with_config(url: impl Into<String>, config: RedisConfig) -> Result<Self> {
78        let url_str = url.into();
79
80        // 使用 from_url 创建配置,然后设置连接池参数
81        let mut cfg = Config::from_url(url_str.clone());
82        cfg.pool = Some(PoolConfig {
83            max_size: config.max_connections,
84            timeouts: Timeouts {
85                wait: Some(Duration::from_secs(config.wait_timeout)),
86                create: Some(Duration::from_secs(config.connect_timeout)),
87                recycle: Some(Duration::from_secs(config.connect_timeout)),
88            },
89            ..Default::default()
90        });
91
92        // 创建连接池
93        let pool = cfg
94            .create_pool(Some(Runtime::Tokio1))
95            .map_err(|e| DbError::RedisConnectionError(format!("创建连接池失败: {}", e)))?;
96
97        // 测试连接
98        let mut conn = pool
99            .get()
100            .await
101            .map_err(|e| DbError::RedisConnectionError(format!("获取连接失败: {}", e)))?;
102
103        // 执行 PING 命令测试连接
104        redis::cmd("PING")
105            .query_async::<String>(&mut *conn)
106            .await
107            .map_err(|e| DbError::RedisConnectionError(format!("连接测试失败: {}", e)))?;
108
109        if config.enable_logging {
110            log::info!("Redis 连接成功: {}", url_str);
111        }
112
113        Ok(Self { pool, config })
114    }
115
116    /// 获取连接池引用
117    ///
118    /// # 返回
119    /// 连接池的引用
120    pub fn pool(&self) -> &Pool {
121        &self.pool
122    }
123
124    /// 创建 Pipeline 批量操作
125    ///
126    /// Pipeline 允许将多个命令打包发送到 Redis 服务器,减少网络往返次数,提高性能。
127    ///
128    /// # 返回
129    /// 新的 Pipeline 实例
130    ///
131    /// # 示例
132    /// ```no_run
133    /// use yang_db::RedisClient;
134    ///
135    /// #[tokio::main]
136    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
137    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
138    ///     
139    ///     let mut pipeline = client.pipeline();
140    ///     pipeline.set("key1", "value1")
141    ///             .set("key2", "value2")
142    ///             .get("key1")
143    ///             .incr("counter");
144    ///
145    ///     let results = pipeline.execute().await?;
146    ///     println!("Pipeline 执行结果: {:?}", results);
147    ///     
148    ///     Ok(())
149    /// }
150    /// ```
151    pub fn pipeline(&self) -> RedisPipeline {
152        RedisPipeline::new(self.clone())
153    }
154
155    /// 创建 Redis 事务
156    ///
157    /// 使用 WATCH/MULTI/EXEC 机制实现乐观锁事务。事务会自动处理 WATCH 冲突并重试。
158    ///
159    /// # 返回
160    /// 新的事务实例
161    ///
162    /// # 示例:基础事务
163    /// ```no_run
164    /// use yang_db::RedisClient;
165    ///
166    /// #[tokio::main]
167    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
168    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
169    ///     
170    ///     let mut tx = client.transaction();
171    ///     tx.set("key1", "value1")
172    ///       .set("key2", "value2")
173    ///       .incr("counter");
174    ///     
175    ///     let results: (String, String, i64) = tx.exec().await?;
176    ///     println!("事务执行结果: {:?}", results);
177    ///     
178    ///     Ok(())
179    /// }
180    /// ```
181    ///
182    /// # 示例:乐观锁实现余额扣减
183    /// ```no_run
184    /// use yang_db::RedisClient;
185    ///
186    /// #[tokio::main]
187    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
188    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
189    ///     
190    ///     // 初始化余额
191    ///     client.set("balance", "1000").await?;
192    ///     
193    ///     // 读取当前余额
194    ///     let balance_str = client.get("balance").await?.unwrap();
195    ///     let balance: i64 = balance_str.parse().unwrap();
196    ///     
197    ///     // 使用事务扣减余额
198    ///     if balance >= 100 {
199    ///         let mut tx = client.transaction();
200    ///         tx.watch(&["balance".to_string()]);
201    ///         tx.set("balance", (balance - 100).to_string());
202    ///         
203    ///         let result: (String,) = tx.exec().await?;
204    ///         println!("余额扣减成功: {:?}", result);
205    ///     }
206    ///     
207    ///     Ok(())
208    /// }
209    /// ```
210    pub fn transaction(&self) -> RedisTransaction {
211        RedisTransaction::new(self.clone())
212    }
213
214    /// 执行 Redis 命令
215    ///
216    /// # 参数
217    /// - `cmd`: Redis 命令
218    ///
219    /// # 返回
220    /// - `Ok(RedisValue)`: 命令执行成功
221    /// - `Err(DbError)`: 命令执行失败
222    ///
223    /// # 示例
224    /// ```no_run
225    /// use yang_db::RedisClient;
226    /// use redis::cmd;
227    ///
228    /// #[tokio::main]
229    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
230    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
231    ///     
232    ///     let mut cmd = cmd("SET");
233    ///     cmd.arg("key").arg("value");
234    ///     let result = client.execute(&cmd).await?;
235    ///     
236    ///     Ok(())
237    /// }
238    /// ```
239    pub async fn execute(&self, cmd: &redis::Cmd) -> Result<RedisValue> {
240        let mut conn = self
241            .pool
242            .get()
243            .await
244            .map_err(|e| DbError::RedisPoolError(format!("获取连接失败: {}", e)))?;
245
246        let value: redis::Value = cmd
247            .query_async(&mut *conn)
248            .await
249            .map_err(|e| DbError::RedisCommandError(format!("命令执行失败: {}", e)))?;
250
251        Ok(RedisValue::from(value))
252    }
253
254    // ==================== String 操作 ====================
255
256    /// SET - 设置键值
257    ///
258    /// # 参数
259    /// - `key`: 键
260    /// - `value`: 值
261    ///
262    /// # 返回
263    /// - `Ok(())`: 设置成功
264    /// - `Err(DbError)`: 设置失败
265    pub async fn set(&self, key: impl Into<String>, value: impl Into<String>) -> Result<()> {
266        let mut cmd = redis::cmd("SET");
267        cmd.arg(key.into()).arg(value.into());
268        self.execute(&cmd).await?;
269        Ok(())
270    }
271
272    /// GET - 获取键的值
273    ///
274    /// # 参数
275    /// - `key`: 键
276    ///
277    /// # 返回
278    /// - `Ok(Some(String))`: 键存在,返回值
279    /// - `Ok(None)`: 键不存在
280    /// - `Err(DbError)`: 获取失败
281    pub async fn get(&self, key: impl Into<String>) -> Result<Option<String>> {
282        let mut cmd = redis::cmd("GET");
283        cmd.arg(key.into());
284        let value = self.execute(&cmd).await?;
285        Ok(value.as_string())
286    }
287
288    /// SETEX - 设置键值并指定过期时间(秒)
289    ///
290    /// # 参数
291    /// - `key`: 键
292    /// - `seconds`: 过期时间(秒)
293    /// - `value`: 值
294    pub async fn setex(
295        &self,
296        key: impl Into<String>,
297        seconds: i64,
298        value: impl Into<String>,
299    ) -> Result<()> {
300        let mut cmd = redis::cmd("SETEX");
301        cmd.arg(key.into()).arg(seconds).arg(value.into());
302        self.execute(&cmd).await?;
303        Ok(())
304    }
305
306    /// SETNX - 仅当键不存在时设置值
307    ///
308    /// # 返回
309    /// - `Ok(true)`: 设置成功
310    /// - `Ok(false)`: 键已存在,未设置
311    pub async fn setnx(&self, key: impl Into<String>, value: impl Into<String>) -> Result<bool> {
312        let mut cmd = redis::cmd("SETNX");
313        cmd.arg(key.into()).arg(value.into());
314        let result = self.execute(&cmd).await?;
315        Ok(result.as_i64() == Some(1))
316    }
317
318    /// GETSET - 设置新值并返回旧值
319    ///
320    /// # 返回
321    /// - `Ok(Some(String))`: 返回旧值
322    /// - `Ok(None)`: 键不存在
323    pub async fn getset(
324        &self,
325        key: impl Into<String>,
326        value: impl Into<String>,
327    ) -> Result<Option<String>> {
328        let mut cmd = redis::cmd("GETSET");
329        cmd.arg(key.into()).arg(value.into());
330        let result = self.execute(&cmd).await?;
331        Ok(result.as_string())
332    }
333
334    /// MGET - 批量获取多个键的值
335    ///
336    /// # 返回
337    /// 返回值数组,不存在的键返回 None
338    pub async fn mget(&self, keys: &[String]) -> Result<Vec<Option<String>>> {
339        let mut cmd = redis::cmd("MGET");
340        for key in keys {
341            cmd.arg(key);
342        }
343        let result = self.execute(&cmd).await?;
344        if let Some(arr) = result.as_array() {
345            Ok(arr.iter().map(|v| v.as_string()).collect())
346        } else {
347            Ok(vec![])
348        }
349    }
350
351    /// MSET - 批量设置多个键值对
352    ///
353    /// # 参数
354    /// - `pairs`: 键值对数组
355    pub async fn mset(&self, pairs: &[(String, String)]) -> Result<()> {
356        let mut cmd = redis::cmd("MSET");
357        for (key, value) in pairs {
358            cmd.arg(key).arg(value);
359        }
360        self.execute(&cmd).await?;
361        Ok(())
362    }
363
364    /// INCR - 将键的值增加 1
365    ///
366    /// # 返回
367    /// 返回增加后的值
368    pub async fn incr(&self, key: impl Into<String>) -> Result<i64> {
369        let mut cmd = redis::cmd("INCR");
370        cmd.arg(key.into());
371        let result = self.execute(&cmd).await?;
372        result
373            .as_i64()
374            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
375    }
376
377    /// INCRBY - 将键的值增加指定数量
378    ///
379    /// # 返回
380    /// 返回增加后的值
381    pub async fn incrby(&self, key: impl Into<String>, increment: i64) -> Result<i64> {
382        let mut cmd = redis::cmd("INCRBY");
383        cmd.arg(key.into()).arg(increment);
384        let result = self.execute(&cmd).await?;
385        result
386            .as_i64()
387            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
388    }
389
390    /// DECR - 将键的值减少 1
391    ///
392    /// # 返回
393    /// 返回减少后的值
394    pub async fn decr(&self, key: impl Into<String>) -> Result<i64> {
395        let mut cmd = redis::cmd("DECR");
396        cmd.arg(key.into());
397        let result = self.execute(&cmd).await?;
398        result
399            .as_i64()
400            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
401    }
402
403    /// DECRBY - 将键的值减少指定数量
404    ///
405    /// # 返回
406    /// 返回减少后的值
407    pub async fn decrby(&self, key: impl Into<String>, decrement: i64) -> Result<i64> {
408        let mut cmd = redis::cmd("DECRBY");
409        cmd.arg(key.into()).arg(decrement);
410        let result = self.execute(&cmd).await?;
411        result
412            .as_i64()
413            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
414    }
415
416    /// APPEND - 将值追加到键的原值末尾
417    ///
418    /// # 返回
419    /// 返回追加后字符串的长度
420    pub async fn append(&self, key: impl Into<String>, value: impl Into<String>) -> Result<i64> {
421        let mut cmd = redis::cmd("APPEND");
422        cmd.arg(key.into()).arg(value.into());
423        let result = self.execute(&cmd).await?;
424        result
425            .as_i64()
426            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
427    }
428
429    /// STRLEN - 获取键存储的字符串长度
430    ///
431    /// # 返回
432    /// 返回字符串长度,键不存在返回 0
433    pub async fn strlen(&self, key: impl Into<String>) -> Result<i64> {
434        let mut cmd = redis::cmd("STRLEN");
435        cmd.arg(key.into());
436        let result = self.execute(&cmd).await?;
437        result
438            .as_i64()
439            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
440    }
441
442    /// GETRANGE - 获取字符串的子串
443    ///
444    /// # 参数
445    /// - `key`: 键
446    /// - `start`: 起始偏移量(0 表示第一个字符,-1 表示最后一个字符)
447    /// - `end`: 结束偏移量(包含)
448    ///
449    /// # 返回
450    /// - `Ok(String)`: 子串内容
451    ///
452    /// # 示例
453    /// ```no_run
454    /// use yang_db::RedisClient;
455    ///
456    /// #[tokio::main]
457    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
458    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
459    ///     
460    ///     // 假设 key "mykey" 的值是 "Hello World"
461    ///     client.set("mykey", "Hello World").await?;
462    ///     let substr = client.getrange("mykey", 0, 4).await?;  // "Hello"
463    ///     let substr2 = client.getrange("mykey", -5, -1).await?; // "World"
464    ///     
465    ///     Ok(())
466    /// }
467    /// ```
468    pub async fn getrange(&self, key: impl Into<String>, start: i64, end: i64) -> Result<String> {
469        let mut cmd = redis::cmd("GETRANGE");
470        cmd.arg(key.into()).arg(start).arg(end);
471        let result = self.execute(&cmd).await?;
472        Ok(result.as_string().unwrap_or_default())
473    }
474
475    /// SETRANGE - 从指定偏移量开始替换字符串内容
476    ///
477    /// # 参数
478    /// - `key`: 键
479    /// - `offset`: 起始偏移量
480    /// - `value`: 要设置的值
481    ///
482    /// # 返回
483    /// - `Ok(i64)`: 修改后字符串的长度
484    ///
485    /// # 示例
486    /// ```no_run
487    /// use yang_db::RedisClient;
488    ///
489    /// #[tokio::main]
490    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
491    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
492    ///     
493    ///     client.set("mykey", "Hello World").await?;
494    ///     let len = client.setrange("mykey", 6, "Redis").await?; // "Hello Redis"
495    ///     
496    ///     Ok(())
497    /// }
498    /// ```
499    pub async fn setrange(
500        &self,
501        key: impl Into<String>,
502        offset: i64,
503        value: impl Into<String>,
504    ) -> Result<i64> {
505        let mut cmd = redis::cmd("SETRANGE");
506        cmd.arg(key.into()).arg(offset).arg(value.into());
507        let result = self.execute(&cmd).await?;
508        result
509            .as_i64()
510            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
511    }
512
513    /// INCRBYFLOAT - 将键的浮点数值增加指定数量
514    ///
515    /// # 参数
516    /// - `key`: 键
517    /// - `increment`: 增量(可以是负数)
518    ///
519    /// # 返回
520    /// - `Ok(f64)`: 增加后的值
521    ///
522    /// # 示例
523    /// ```no_run
524    /// use yang_db::RedisClient;
525    ///
526    /// #[tokio::main]
527    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
528    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
529    ///     
530    ///     client.set("price", "10.5").await?;
531    ///     let new_price = client.incrbyfloat("price", 2.3).await?; // 12.8
532    ///     
533    ///     Ok(())
534    /// }
535    /// ```
536    pub async fn incrbyfloat(&self, key: impl Into<String>, increment: f64) -> Result<f64> {
537        let mut cmd = redis::cmd("INCRBYFLOAT");
538        cmd.arg(key.into()).arg(increment);
539        let result = self.execute(&cmd).await?;
540        // Redis 返回字符串形式的浮点数
541        if let Some(s) = result.as_string() {
542            s.parse::<f64>()
543                .map_err(|_| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
544        } else {
545            result
546                .as_f64()
547                .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
548        }
549    }
550
551    /// PSETEX - 设置键值并指定毫秒级过期时间
552    ///
553    /// # 参数
554    /// - `key`: 键
555    /// - `milliseconds`: 过期时间(毫秒)
556    /// - `value`: 值
557    ///
558    /// # 示例
559    /// ```no_run
560    /// use yang_db::RedisClient;
561    ///
562    /// #[tokio::main]
563    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
564    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
565    ///     
566    ///     // 设置键值,100 毫秒后过期
567    ///     client.psetex("session", 100, "data").await?;
568    ///     
569    ///     Ok(())
570    /// }
571    /// ```
572    pub async fn psetex(
573        &self,
574        key: impl Into<String>,
575        milliseconds: i64,
576        value: impl Into<String>,
577    ) -> Result<()> {
578        let mut cmd = redis::cmd("PSETEX");
579        cmd.arg(key.into()).arg(milliseconds).arg(value.into());
580        self.execute(&cmd).await?;
581        Ok(())
582    }
583
584    // ==================== Hash 操作 ====================
585
586    /// HSET - 设置哈希表字段的值
587    ///
588    /// # 返回
589    /// - `Ok(true)`: 新字段被设置
590    /// - `Ok(false)`: 字段已存在,值被更新
591    pub async fn hset(
592        &self,
593        key: impl Into<String>,
594        field: impl Into<String>,
595        value: impl Into<String>,
596    ) -> Result<bool> {
597        let mut cmd = redis::cmd("HSET");
598        cmd.arg(key.into()).arg(field.into()).arg(value.into());
599        let result = self.execute(&cmd).await?;
600        Ok(result.as_i64() == Some(1))
601    }
602
603    /// HGET - 获取哈希表字段的值
604    ///
605    /// # 返回
606    /// - `Ok(Some(String))`: 字段存在
607    /// - `Ok(None)`: 字段不存在
608    pub async fn hget(
609        &self,
610        key: impl Into<String>,
611        field: impl Into<String>,
612    ) -> Result<Option<String>> {
613        let mut cmd = redis::cmd("HGET");
614        cmd.arg(key.into()).arg(field.into());
615        let result = self.execute(&cmd).await?;
616        Ok(result.as_string())
617    }
618
619    /// HDEL - 删除哈希表的一个或多个字段
620    ///
621    /// # 返回
622    /// 返回被删除字段的数量
623    pub async fn hdel(&self, key: impl Into<String>, fields: &[String]) -> Result<i64> {
624        let mut cmd = redis::cmd("HDEL");
625        cmd.arg(key.into());
626        for field in fields {
627            cmd.arg(field);
628        }
629        let result = self.execute(&cmd).await?;
630        result
631            .as_i64()
632            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
633    }
634
635    /// HEXISTS - 检查哈希表字段是否存在
636    ///
637    /// # 返回
638    /// - `Ok(true)`: 字段存在
639    /// - `Ok(false)`: 字段不存在
640    pub async fn hexists(&self, key: impl Into<String>, field: impl Into<String>) -> Result<bool> {
641        let mut cmd = redis::cmd("HEXISTS");
642        cmd.arg(key.into()).arg(field.into());
643        let result = self.execute(&cmd).await?;
644        Ok(result.as_i64() == Some(1))
645    }
646
647    /// HMSET - 批量设置哈希表的多个字段
648    pub async fn hmset(&self, key: impl Into<String>, fields: &[(String, String)]) -> Result<()> {
649        let mut cmd = redis::cmd("HMSET");
650        cmd.arg(key.into());
651        for (field, value) in fields {
652            cmd.arg(field).arg(value);
653        }
654        self.execute(&cmd).await?;
655        Ok(())
656    }
657
658    /// HMGET - 批量获取哈希表的多个字段值
659    ///
660    /// # 返回
661    /// 返回值数组,不存在的字段返回 None
662    pub async fn hmget(
663        &self,
664        key: impl Into<String>,
665        fields: &[String],
666    ) -> Result<Vec<Option<String>>> {
667        let mut cmd = redis::cmd("HMGET");
668        cmd.arg(key.into());
669        for field in fields {
670            cmd.arg(field);
671        }
672        let result = self.execute(&cmd).await?;
673        if let Some(arr) = result.as_array() {
674            Ok(arr.iter().map(|v| v.as_string()).collect())
675        } else {
676            Ok(vec![])
677        }
678    }
679
680    /// HGETALL - 获取哈希表的所有字段和值
681    ///
682    /// # 返回
683    /// 返回字段-值对的向量
684    pub async fn hgetall(&self, key: impl Into<String>) -> Result<Vec<(String, String)>> {
685        let mut cmd = redis::cmd("HGETALL");
686        cmd.arg(key.into());
687        let result = self.execute(&cmd).await?;
688        if let Some(arr) = result.as_array() {
689            let mut pairs = Vec::new();
690            for i in (0..arr.len()).step_by(2) {
691                if i + 1 < arr.len() {
692                    if let (Some(field), Some(value)) = (arr[i].as_string(), arr[i + 1].as_string()) {
693                        pairs.push((field, value));
694                    }
695                }
696            }
697            Ok(pairs)
698        } else {
699            Ok(vec![])
700        }
701    }
702
703    /// HLEN - 获取哈希表的字段数量
704    pub async fn hlen(&self, key: impl Into<String>) -> Result<i64> {
705        let mut cmd = redis::cmd("HLEN");
706        cmd.arg(key.into());
707        let result = self.execute(&cmd).await?;
708        result
709            .as_i64()
710            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
711    }
712
713    /// HKEYS - 获取哈希表的所有字段名
714    pub async fn hkeys(&self, key: impl Into<String>) -> Result<Vec<String>> {
715        let mut cmd = redis::cmd("HKEYS");
716        cmd.arg(key.into());
717        let result = self.execute(&cmd).await?;
718        if let Some(arr) = result.as_array() {
719            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
720        } else {
721            Ok(vec![])
722        }
723    }
724
725    /// HVALS - 获取哈希表的所有值
726    pub async fn hvals(&self, key: impl Into<String>) -> Result<Vec<String>> {
727        let mut cmd = redis::cmd("HVALS");
728        cmd.arg(key.into());
729        let result = self.execute(&cmd).await?;
730        if let Some(arr) = result.as_array() {
731            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
732        } else {
733            Ok(vec![])
734        }
735    }
736
737    /// HINCRBY - 将哈希表字段的整数值增加指定数量
738    pub async fn hincrby(
739        &self,
740        key: impl Into<String>,
741        field: impl Into<String>,
742        increment: i64,
743    ) -> Result<i64> {
744        let mut cmd = redis::cmd("HINCRBY");
745        cmd.arg(key.into()).arg(field.into()).arg(increment);
746        let result = self.execute(&cmd).await?;
747        result
748            .as_i64()
749            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
750    }
751
752    /// HINCRBYFLOAT - 将哈希表字段的浮点数值增加指定数量
753    pub async fn hincrbyfloat(
754        &self,
755        key: impl Into<String>,
756        field: impl Into<String>,
757        increment: f64,
758    ) -> Result<f64> {
759        let mut cmd = redis::cmd("HINCRBYFLOAT");
760        cmd.arg(key.into()).arg(field.into()).arg(increment);
761        let result = self.execute(&cmd).await?;
762        // Redis 返回字符串形式的浮点数
763        if let Some(s) = result.as_string() {
764            s.parse::<f64>()
765                .map_err(|_| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
766        } else {
767            result
768                .as_f64()
769                .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
770        }
771    }
772
773    // ==================== List 操作 ====================
774
775    /// LPUSH - 将一个或多个值插入列表头部
776    ///
777    /// # 返回
778    /// 返回插入后列表的长度
779    pub async fn lpush(&self, key: impl Into<String>, values: &[String]) -> Result<i64> {
780        let mut cmd = redis::cmd("LPUSH");
781        cmd.arg(key.into());
782        for value in values {
783            cmd.arg(value);
784        }
785        let result = self.execute(&cmd).await?;
786        result
787            .as_i64()
788            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
789    }
790
791    /// RPUSH - 将一个或多个值插入列表尾部
792    ///
793    /// # 返回
794    /// 返回插入后列表的长度
795    pub async fn rpush(&self, key: impl Into<String>, values: &[String]) -> Result<i64> {
796        let mut cmd = redis::cmd("RPUSH");
797        cmd.arg(key.into());
798        for value in values {
799            cmd.arg(value);
800        }
801        let result = self.execute(&cmd).await?;
802        result
803            .as_i64()
804            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
805    }
806
807    /// LPOP - 移除并返回列表的头元素
808    ///
809    /// # 返回
810    /// - `Ok(Some(String))`: 返回头元素
811    /// - `Ok(None)`: 列表为空
812    pub async fn lpop(&self, key: impl Into<String>) -> Result<Option<String>> {
813        let mut cmd = redis::cmd("LPOP");
814        cmd.arg(key.into());
815        let result = self.execute(&cmd).await?;
816        Ok(result.as_string())
817    }
818
819    /// RPOP - 移除并返回列表的尾元素
820    ///
821    /// # 返回
822    /// - `Ok(Some(String))`: 返回尾元素
823    /// - `Ok(None)`: 列表为空
824    pub async fn rpop(&self, key: impl Into<String>) -> Result<Option<String>> {
825        let mut cmd = redis::cmd("RPOP");
826        cmd.arg(key.into());
827        let result = self.execute(&cmd).await?;
828        Ok(result.as_string())
829    }
830
831    /// LRANGE - 获取列表指定范围内的元素
832    ///
833    /// # 参数
834    /// - `start`: 起始索引(0 表示第一个元素)
835    /// - `stop`: 结束索引(-1 表示最后一个元素)
836    pub async fn lrange(
837        &self,
838        key: impl Into<String>,
839        start: i64,
840        stop: i64,
841    ) -> Result<Vec<String>> {
842        let mut cmd = redis::cmd("LRANGE");
843        cmd.arg(key.into()).arg(start).arg(stop);
844        let result = self.execute(&cmd).await?;
845        if let Some(arr) = result.as_array() {
846            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
847        } else {
848            Ok(vec![])
849        }
850    }
851
852    /// LLEN - 获取列表长度
853    pub async fn llen(&self, key: impl Into<String>) -> Result<i64> {
854        let mut cmd = redis::cmd("LLEN");
855        cmd.arg(key.into());
856        let result = self.execute(&cmd).await?;
857        result
858            .as_i64()
859            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
860    }
861
862    /// LINDEX - 获取列表指定索引的元素
863    ///
864    /// # 返回
865    /// - `Ok(Some(String))`: 返回元素
866    /// - `Ok(None)`: 索引超出范围
867    pub async fn lindex(&self, key: impl Into<String>, index: i64) -> Result<Option<String>> {
868        let mut cmd = redis::cmd("LINDEX");
869        cmd.arg(key.into()).arg(index);
870        let result = self.execute(&cmd).await?;
871        Ok(result.as_string())
872    }
873
874    /// LSET - 设置列表指定索引的元素值
875    pub async fn lset(
876        &self,
877        key: impl Into<String>,
878        index: i64,
879        value: impl Into<String>,
880    ) -> Result<()> {
881        let mut cmd = redis::cmd("LSET");
882        cmd.arg(key.into()).arg(index).arg(value.into());
883        self.execute(&cmd).await?;
884        Ok(())
885    }
886
887    /// LTRIM - 修剪列表,仅保留指定范围内的元素
888    pub async fn ltrim(&self, key: impl Into<String>, start: i64, stop: i64) -> Result<()> {
889        let mut cmd = redis::cmd("LTRIM");
890        cmd.arg(key.into()).arg(start).arg(stop);
891        self.execute(&cmd).await?;
892        Ok(())
893    }
894
895    /// LINSERT - 在列表的指定元素前或后插入新元素
896    ///
897    /// # 参数
898    /// - `key`: 键
899    /// - `before_after`: "BEFORE" 或 "AFTER"
900    /// - `pivot`: 参考元素
901    /// - `value`: 要插入的值
902    ///
903    /// # 返回
904    /// - `Ok(i64)`: 插入后列表的长度,-1 表示 pivot 不存在
905    ///
906    /// # 示例
907    /// ```no_run
908    /// use yang_db::RedisClient;
909    ///
910    /// #[tokio::main]
911    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
912    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
913    ///     
914    ///     client.rpush("mylist", &["a".to_string(), "c".to_string()]).await?;
915    ///     client.linsert("mylist", "BEFORE", "c", "b").await?; // ["a", "b", "c"]
916    ///     
917    ///     Ok(())
918    /// }
919    /// ```
920    pub async fn linsert(
921        &self,
922        key: impl Into<String>,
923        before_after: &str,
924        pivot: impl Into<String>,
925        value: impl Into<String>,
926    ) -> Result<i64> {
927        let mut cmd = redis::cmd("LINSERT");
928        cmd.arg(key.into())
929            .arg(before_after)
930            .arg(pivot.into())
931            .arg(value.into());
932        let result = self.execute(&cmd).await?;
933        result
934            .as_i64()
935            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
936    }
937
938    /// LREM - 删除列表中的指定元素
939    ///
940    /// # 参数
941    /// - `key`: 键
942    /// - `count`: 删除数量
943    ///   - count > 0: 从头到尾删除 count 个匹配元素
944    ///   - count < 0: 从尾到头删除 |count| 个匹配元素
945    ///   - count = 0: 删除所有匹配元素
946    /// - `value`: 要删除的值
947    ///
948    /// # 返回
949    /// - `Ok(i64)`: 被删除元素的数量
950    ///
951    /// # 示例
952    /// ```no_run
953    /// use yang_db::RedisClient;
954    ///
955    /// #[tokio::main]
956    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
957    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
958    ///     
959    ///     client.rpush("mylist", &["a".to_string(), "b".to_string(), "a".to_string()]).await?;
960    ///     let removed = client.lrem("mylist", 2, "a").await?; // 删除 2 个 "a"
961    ///     
962    ///     Ok(())
963    /// }
964    /// ```
965    pub async fn lrem(
966        &self,
967        key: impl Into<String>,
968        count: i64,
969        value: impl Into<String>,
970    ) -> Result<i64> {
971        let mut cmd = redis::cmd("LREM");
972        cmd.arg(key.into()).arg(count).arg(value.into());
973        let result = self.execute(&cmd).await?;
974        result
975            .as_i64()
976            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
977    }
978
979    /// RPOPLPUSH - 从源列表尾部弹出元素并插入到目标列表头部
980    ///
981    /// # 参数
982    /// - `source`: 源列表键
983    /// - `destination`: 目标列表键
984    ///
985    /// # 返回
986    /// - `Ok(Some(String))`: 被移动的元素
987    /// - `Ok(None)`: 源列表为空
988    ///
989    /// # 示例
990    /// ```no_run
991    /// use yang_db::RedisClient;
992    ///
993    /// #[tokio::main]
994    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
995    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
996    ///     
997    ///     client.rpush("list1", &["a".to_string(), "b".to_string()]).await?;
998    ///     let elem = client.rpoplpush("list1", "list2").await?; // "b"
999    ///     
1000    ///     Ok(())
1001    /// }
1002    /// ```
1003    pub async fn rpoplpush(
1004        &self,
1005        source: impl Into<String>,
1006        destination: impl Into<String>,
1007    ) -> Result<Option<String>> {
1008        let mut cmd = redis::cmd("RPOPLPUSH");
1009        cmd.arg(source.into()).arg(destination.into());
1010        let result = self.execute(&cmd).await?;
1011        Ok(result.as_string())
1012    }
1013
1014    /// BLPOP - 阻塞式地从列表头部弹出元素
1015    ///
1016    /// # 参数
1017    /// - `keys`: 键列表(按顺序检查)
1018    /// - `timeout`: 超时时间(秒),0 表示无限等待
1019    ///
1020    /// # 返回
1021    /// - `Ok(Some((String, String)))`: (键名, 元素值)
1022    /// - `Ok(None)`: 超时
1023    ///
1024    /// # 示例
1025    /// ```no_run
1026    /// use yang_db::RedisClient;
1027    ///
1028    /// #[tokio::main]
1029    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
1030    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
1031    ///     
1032    ///     let result = client.blpop(&["queue1".to_string(), "queue2".to_string()], 5).await?;
1033    ///     if let Some((key, value)) = result {
1034    ///         println!("从 {} 弹出: {}", key, value);
1035    ///     }
1036    ///     
1037    ///     Ok(())
1038    /// }
1039    /// ```
1040    pub async fn blpop(&self, keys: &[String], timeout: i64) -> Result<Option<(String, String)>> {
1041        let mut cmd = redis::cmd("BLPOP");
1042        for key in keys {
1043            cmd.arg(key);
1044        }
1045        cmd.arg(timeout);
1046        let result = self.execute(&cmd).await?;
1047
1048        // BLPOP 返回数组 [key, value] 或 nil
1049        if let Some(arr) = result.as_array() {
1050            if arr.len() == 2 {
1051                if let (Some(key), Some(value)) = (arr[0].as_string(), arr[1].as_string()) {
1052                    return Ok(Some((key, value)));
1053                }
1054            }
1055        }
1056        Ok(None)
1057    }
1058
1059    /// BRPOP - 阻塞式地从列表尾部弹出元素
1060    ///
1061    /// # 参数
1062    /// - `keys`: 键列表(按顺序检查)
1063    /// - `timeout`: 超时时间(秒),0 表示无限等待
1064    ///
1065    /// # 返回
1066    /// - `Ok(Some((String, String)))`: (键名, 元素值)
1067    /// - `Ok(None)`: 超时
1068    ///
1069    /// # 示例
1070    /// ```no_run
1071    /// use yang_db::RedisClient;
1072    ///
1073    /// #[tokio::main]
1074    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
1075    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
1076    ///     
1077    ///     let result = client.brpop(&["queue1".to_string()], 10).await?;
1078    ///     
1079    ///     Ok(())
1080    /// }
1081    /// ```
1082    pub async fn brpop(&self, keys: &[String], timeout: i64) -> Result<Option<(String, String)>> {
1083        let mut cmd = redis::cmd("BRPOP");
1084        for key in keys {
1085            cmd.arg(key);
1086        }
1087        cmd.arg(timeout);
1088        let result = self.execute(&cmd).await?;
1089
1090        // BRPOP 返回数组 [key, value] 或 nil
1091        if let Some(arr) = result.as_array() {
1092            if arr.len() == 2 {
1093                if let (Some(key), Some(value)) = (arr[0].as_string(), arr[1].as_string()) {
1094                    return Ok(Some((key, value)));
1095                }
1096            }
1097        }
1098        Ok(None)
1099    }
1100
1101    // ==================== Set 操作 ====================
1102
1103    /// SADD - 向集合添加一个或多个成员
1104    ///
1105    /// # 返回
1106    /// 返回被添加到集合中的新元素数量
1107    pub async fn sadd(&self, key: impl Into<String>, members: &[String]) -> Result<i64> {
1108        let mut cmd = redis::cmd("SADD");
1109        cmd.arg(key.into());
1110        for member in members {
1111            cmd.arg(member);
1112        }
1113        let result = self.execute(&cmd).await?;
1114        result
1115            .as_i64()
1116            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1117    }
1118
1119    /// SREM - 移除集合中的一个或多个成员
1120    ///
1121    /// # 返回
1122    /// 返回被移除的元素数量
1123    pub async fn srem(&self, key: impl Into<String>, members: &[String]) -> Result<i64> {
1124        let mut cmd = redis::cmd("SREM");
1125        cmd.arg(key.into());
1126        for member in members {
1127            cmd.arg(member);
1128        }
1129        let result = self.execute(&cmd).await?;
1130        result
1131            .as_i64()
1132            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1133    }
1134
1135    /// SMEMBERS - 获取集合的所有成员
1136    pub async fn smembers(&self, key: impl Into<String>) -> Result<Vec<String>> {
1137        let mut cmd = redis::cmd("SMEMBERS");
1138        cmd.arg(key.into());
1139        let result = self.execute(&cmd).await?;
1140        if let Some(arr) = result.as_array() {
1141            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
1142        } else {
1143            Ok(vec![])
1144        }
1145    }
1146
1147    /// SISMEMBER - 检查元素是否是集合的成员
1148    ///
1149    /// # 返回
1150    /// - `Ok(true)`: 元素是集合成员
1151    /// - `Ok(false)`: 元素不是集合成员
1152    pub async fn sismember(
1153        &self,
1154        key: impl Into<String>,
1155        member: impl Into<String>,
1156    ) -> Result<bool> {
1157        let mut cmd = redis::cmd("SISMEMBER");
1158        cmd.arg(key.into()).arg(member.into());
1159        let result = self.execute(&cmd).await?;
1160        Ok(result.as_i64() == Some(1))
1161    }
1162
1163    /// SCARD - 获取集合的成员数量
1164    pub async fn scard(&self, key: impl Into<String>) -> Result<i64> {
1165        let mut cmd = redis::cmd("SCARD");
1166        cmd.arg(key.into());
1167        let result = self.execute(&cmd).await?;
1168        result
1169            .as_i64()
1170            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1171    }
1172
1173    /// SPOP - 移除并返回集合中的一个随机元素
1174    ///
1175    /// # 返回
1176    /// - `Ok(Some(String))`: 返回被移除的元素
1177    /// - `Ok(None)`: 集合为空
1178    pub async fn spop(&self, key: impl Into<String>) -> Result<Option<String>> {
1179        let mut cmd = redis::cmd("SPOP");
1180        cmd.arg(key.into());
1181        let result = self.execute(&cmd).await?;
1182        Ok(result.as_string())
1183    }
1184
1185    /// SRANDMEMBER - 返回集合中的一个随机元素(不移除)
1186    ///
1187    /// # 返回
1188    /// - `Ok(Some(String))`: 返回随机元素
1189    /// - `Ok(None)`: 集合为空
1190    pub async fn srandmember(&self, key: impl Into<String>) -> Result<Option<String>> {
1191        let mut cmd = redis::cmd("SRANDMEMBER");
1192        cmd.arg(key.into());
1193        let result = self.execute(&cmd).await?;
1194        Ok(result.as_string())
1195    }
1196
1197    /// SINTER - 返回多个集合的交集
1198    ///
1199    /// 计算所有给定集合的共同成员。
1200    ///
1201    /// # 参数
1202    /// - `keys`: 参与交集运算的集合键列表
1203    ///
1204    /// # 返回
1205    /// 所有集合中都存在的成员列表
1206    ///
1207    /// # 示例
1208    /// ```no_run
1209    /// # use yang_db::RedisClient;
1210    /// # async fn example() -> Result<(), yang_db::DbError> {
1211    /// # let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
1212    /// let common = client.sinter(&["set_a".to_string(), "set_b".to_string()]).await?;
1213    /// # Ok(())
1214    /// # }
1215    /// ```
1216    pub async fn sinter(&self, keys: &[String]) -> Result<Vec<String>> {
1217        let mut cmd = redis::cmd("SINTER");
1218        for key in keys {
1219            cmd.arg(key);
1220        }
1221        let result = self.execute(&cmd).await?;
1222        if let Some(arr) = result.as_array() {
1223            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
1224        } else {
1225            Ok(vec![])
1226        }
1227    }
1228
1229    /// SUNION - 返回多个集合的并集
1230    ///
1231    /// 计算所有给定集合的所有成员。
1232    ///
1233    /// # 参数
1234    /// - `keys`: 参与并集运算的集合键列表
1235    ///
1236    /// # 返回
1237    /// 所有集合的成员合并列表(去重)
1238    pub async fn sunion(&self, keys: &[String]) -> Result<Vec<String>> {
1239        let mut cmd = redis::cmd("SUNION");
1240        for key in keys {
1241            cmd.arg(key);
1242        }
1243        let result = self.execute(&cmd).await?;
1244        if let Some(arr) = result.as_array() {
1245            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
1246        } else {
1247            Ok(vec![])
1248        }
1249    }
1250
1251    /// SDIFF - 返回多个集合的差集
1252    ///
1253    /// 计算第一个集合与其余集合的差集(仅存在于第一个集合中的成员)。
1254    ///
1255    /// # 参数
1256    /// - `keys`: 集合键列表,第一个为基准集合
1257    ///
1258    /// # 返回
1259    /// 仅存在于第一个集合中的成员列表
1260    pub async fn sdiff(&self, keys: &[String]) -> Result<Vec<String>> {
1261        let mut cmd = redis::cmd("SDIFF");
1262        for key in keys {
1263            cmd.arg(key);
1264        }
1265        let result = self.execute(&cmd).await?;
1266        if let Some(arr) = result.as_array() {
1267            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
1268        } else {
1269            Ok(vec![])
1270        }
1271    }
1272
1273    /// SMOVE - 将指定成员从源集合移动到目标集合
1274    ///
1275    /// # 参数
1276    /// - `source`: 源集合键
1277    /// - `destination`: 目标集合键
1278    /// - `member`: 要移动的成员
1279    ///
1280    /// # 返回
1281    /// - `Ok(true)`: 移动成功
1282    /// - `Ok(false)`: 成员不存在于源集合
1283    pub async fn smove(
1284        &self,
1285        source: impl Into<String>,
1286        destination: impl Into<String>,
1287        member: impl Into<String>,
1288    ) -> Result<bool> {
1289        let mut cmd = redis::cmd("SMOVE");
1290        cmd.arg(source.into())
1291            .arg(destination.into())
1292            .arg(member.into());
1293        let result = self.execute(&cmd).await?;
1294        Ok(result.as_i64() == Some(1))
1295    }
1296
1297    /// SSCAN - 增量式迭代集合中的元素
1298    ///
1299    /// # 参数
1300    /// - `key`: 集合键
1301    /// - `cursor`: 游标(初始传 0)
1302    /// - `pattern`: 可选的匹配模式
1303    /// - `count`: 可选的每批返回数量提示
1304    ///
1305    /// # 返回
1306    /// - `Ok((i64, Vec<String>))`: (下一游标, 本批成员列表)
1307    pub async fn sscan(
1308        &self,
1309        key: impl Into<String>,
1310        cursor: i64,
1311        pattern: Option<&str>,
1312        count: Option<i64>,
1313    ) -> Result<(i64, Vec<String>)> {
1314        let mut cmd = redis::cmd("SSCAN");
1315        cmd.arg(key.into()).arg(cursor);
1316        if let Some(p) = pattern {
1317            cmd.arg("MATCH").arg(p);
1318        }
1319        if let Some(c) = count {
1320            cmd.arg("COUNT").arg(c);
1321        }
1322        let result = self.execute(&cmd).await?;
1323        parse_scan_result(&result)
1324    }
1325
1326    // ==================== Sorted Set 操作 ====================
1327
1328    /// ZADD - 向有序集合添加一个或多个成员
1329    ///
1330    /// # 返回
1331    /// 返回被添加的新成员数量
1332    pub async fn zadd(&self, key: impl Into<String>, members: &[(f64, String)]) -> Result<i64> {
1333        let mut cmd = redis::cmd("ZADD");
1334        cmd.arg(key.into());
1335        for (score, member) in members {
1336            cmd.arg(score).arg(member);
1337        }
1338        let result = self.execute(&cmd).await?;
1339        result
1340            .as_i64()
1341            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1342    }
1343
1344    /// ZREM - 移除有序集合中的一个或多个成员
1345    ///
1346    /// # 返回
1347    /// 返回被移除的成员数量
1348    pub async fn zrem(&self, key: impl Into<String>, members: &[String]) -> Result<i64> {
1349        let mut cmd = redis::cmd("ZREM");
1350        cmd.arg(key.into());
1351        for member in members {
1352            cmd.arg(member);
1353        }
1354        let result = self.execute(&cmd).await?;
1355        result
1356            .as_i64()
1357            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1358    }
1359
1360    /// ZSCORE - 获取成员的分数
1361    ///
1362    /// # 返回
1363    /// - `Ok(Some(f64))`: 返回分数
1364    /// - `Ok(None)`: 成员不存在
1365    pub async fn zscore(
1366        &self,
1367        key: impl Into<String>,
1368        member: impl Into<String>,
1369    ) -> Result<Option<f64>> {
1370        let mut cmd = redis::cmd("ZSCORE");
1371        cmd.arg(key.into()).arg(member.into());
1372        let result = self.execute(&cmd).await?;
1373        if let Some(s) = result.as_string() {
1374            Ok(s.parse::<f64>().ok())
1375        } else {
1376            Ok(result.as_f64())
1377        }
1378    }
1379
1380    /// ZCARD - 获取有序集合的成员数量
1381    pub async fn zcard(&self, key: impl Into<String>) -> Result<i64> {
1382        let mut cmd = redis::cmd("ZCARD");
1383        cmd.arg(key.into());
1384        let result = self.execute(&cmd).await?;
1385        result
1386            .as_i64()
1387            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1388    }
1389
1390    /// ZRANGE - 按索引范围获取有序集合的成员
1391    ///
1392    /// # 参数
1393    /// - `start`: 起始索引
1394    /// - `stop`: 结束索引
1395    pub async fn zrange(
1396        &self,
1397        key: impl Into<String>,
1398        start: i64,
1399        stop: i64,
1400    ) -> Result<Vec<String>> {
1401        let mut cmd = redis::cmd("ZRANGE");
1402        cmd.arg(key.into()).arg(start).arg(stop);
1403        let result = self.execute(&cmd).await?;
1404        if let Some(arr) = result.as_array() {
1405            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
1406        } else {
1407            Ok(vec![])
1408        }
1409    }
1410
1411    /// ZRANGEBYSCORE - 按分数范围获取有序集合的成员
1412    ///
1413    /// # 参数
1414    /// - `min`: 最小分数
1415    /// - `max`: 最大分数
1416    pub async fn zrangebyscore(
1417        &self,
1418        key: impl Into<String>,
1419        min: f64,
1420        max: f64,
1421    ) -> Result<Vec<String>> {
1422        let mut cmd = redis::cmd("ZRANGEBYSCORE");
1423        cmd.arg(key.into()).arg(min).arg(max);
1424        let result = self.execute(&cmd).await?;
1425        if let Some(arr) = result.as_array() {
1426            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
1427        } else {
1428            Ok(vec![])
1429        }
1430    }
1431
1432    /// ZCOUNT - 计算分数范围内的成员数量
1433    pub async fn zcount(&self, key: impl Into<String>, min: f64, max: f64) -> Result<i64> {
1434        let mut cmd = redis::cmd("ZCOUNT");
1435        cmd.arg(key.into()).arg(min).arg(max);
1436        let result = self.execute(&cmd).await?;
1437        result
1438            .as_i64()
1439            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1440    }
1441
1442    /// ZINCRBY - 将成员的分数增加指定数量
1443    ///
1444    /// # 返回
1445    /// 返回增加后的分数
1446    pub async fn zincrby(
1447        &self,
1448        key: impl Into<String>,
1449        increment: f64,
1450        member: impl Into<String>,
1451    ) -> Result<f64> {
1452        let mut cmd = redis::cmd("ZINCRBY");
1453        cmd.arg(key.into()).arg(increment).arg(member.into());
1454        let result = self.execute(&cmd).await?;
1455        if let Some(s) = result.as_string() {
1456            s.parse::<f64>()
1457                .map_err(|_| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
1458        } else {
1459            result
1460                .as_f64()
1461                .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为浮点数".to_string()))
1462        }
1463    }
1464
1465    /// ZRANK - 获取成员在有序集合中的排名(从低到高,0 为第一名)
1466    ///
1467    /// # 参数
1468    /// - `key`: 有序集合键
1469    /// - `member`: 成员
1470    ///
1471    /// # 返回
1472    /// - `Ok(Some(i64))`: 成员的排名(0 开始)
1473    /// - `Ok(None)`: 成员不存在
1474    pub async fn zrank(
1475        &self,
1476        key: impl Into<String>,
1477        member: impl Into<String>,
1478    ) -> Result<Option<i64>> {
1479        let mut cmd = redis::cmd("ZRANK");
1480        cmd.arg(key.into()).arg(member.into());
1481        let result = self.execute(&cmd).await?;
1482        if result.is_nil() {
1483            Ok(None)
1484        } else {
1485            Ok(result.as_i64())
1486        }
1487    }
1488
1489    /// ZREVRANK - 获取成员在有序集合中的逆序排名(从高到低,0 为第一名)
1490    ///
1491    /// # 参数
1492    /// - `key`: 有序集合键
1493    /// - `member`: 成员
1494    ///
1495    /// # 返回
1496    /// - `Ok(Some(i64))`: 成员的逆序排名(0 开始)
1497    /// - `Ok(None)`: 成员不存在
1498    pub async fn zrevrank(
1499        &self,
1500        key: impl Into<String>,
1501        member: impl Into<String>,
1502    ) -> Result<Option<i64>> {
1503        let mut cmd = redis::cmd("ZREVRANK");
1504        cmd.arg(key.into()).arg(member.into());
1505        let result = self.execute(&cmd).await?;
1506        if result.is_nil() {
1507            Ok(None)
1508        } else {
1509            Ok(result.as_i64())
1510        }
1511    }
1512
1513    /// ZREVRANGE - 按索引范围从高到低获取有序集合的成员
1514    ///
1515    /// # 参数
1516    /// - `key`: 有序集合键
1517    /// - `start`: 起始索引
1518    /// - `stop`: 结束索引
1519    ///
1520    /// # 返回
1521    /// 指定范围内的成员列表(从高分到低分排序)
1522    pub async fn zrevrange(
1523        &self,
1524        key: impl Into<String>,
1525        start: i64,
1526        stop: i64,
1527    ) -> Result<Vec<String>> {
1528        let mut cmd = redis::cmd("ZREVRANGE");
1529        cmd.arg(key.into()).arg(start).arg(stop);
1530        let result = self.execute(&cmd).await?;
1531        if let Some(arr) = result.as_array() {
1532            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
1533        } else {
1534            Ok(vec![])
1535        }
1536    }
1537
1538    /// ZRANGE WITHSCORES - 按索引范围获取有序集合的成员及其分数
1539    ///
1540    /// # 参数
1541    /// - `key`: 有序集合键
1542    /// - `start`: 起始索引
1543    /// - `stop`: 结束索引
1544    ///
1545    /// # 返回
1546    /// 成员和分数的元组列表(从低分到高分排序)
1547    pub async fn zrange_with_scores(
1548        &self,
1549        key: impl Into<String>,
1550        start: i64,
1551        stop: i64,
1552    ) -> Result<Vec<(String, f64)>> {
1553        let mut cmd = redis::cmd("ZRANGE");
1554        cmd.arg(key.into()).arg(start).arg(stop).arg("WITHSCORES");
1555        let result = self.execute(&cmd).await?;
1556        Ok(parse_with_scores(&result))
1557    }
1558
1559    /// ZREVRANGE WITHSCORES - 按索引范围从高到低获取有序集合的成员及其分数
1560    ///
1561    /// # 参数
1562    /// - `key`: 有序集合键
1563    /// - `start`: 起始索引
1564    /// - `stop`: 结束索引
1565    ///
1566    /// # 返回
1567    /// 成员和分数的元组列表(从高分到低分排序)
1568    pub async fn zrevrange_with_scores(
1569        &self,
1570        key: impl Into<String>,
1571        start: i64,
1572        stop: i64,
1573    ) -> Result<Vec<(String, f64)>> {
1574        let mut cmd = redis::cmd("ZREVRANGE");
1575        cmd.arg(key.into()).arg(start).arg(stop).arg("WITHSCORES");
1576        let result = self.execute(&cmd).await?;
1577        Ok(parse_with_scores(&result))
1578    }
1579
1580    /// ZREMRANGEBYRANK - 移除有序集合中指定排名范围的成员
1581    ///
1582    /// # 参数
1583    /// - `key`: 有序集合键
1584    /// - `start`: 起始排名(0 为第一)
1585    /// - `stop`: 结束排名(-1 为最后一个)
1586    ///
1587    /// # 返回
1588    /// 被移除的成员数量
1589    pub async fn zremrangebyrank(
1590        &self,
1591        key: impl Into<String>,
1592        start: i64,
1593        stop: i64,
1594    ) -> Result<i64> {
1595        let mut cmd = redis::cmd("ZREMRANGEBYRANK");
1596        cmd.arg(key.into()).arg(start).arg(stop);
1597        let result = self.execute(&cmd).await?;
1598        result
1599            .as_i64()
1600            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1601    }
1602
1603    /// ZREMRANGEBYSCORE - 移除有序集合中指定分数范围的成员
1604    ///
1605    /// # 参数
1606    /// - `key`: 有序集合键
1607    /// - `min`: 最小分数(包含)
1608    /// - `max`: 最大分数(包含)
1609    ///
1610    /// # 返回
1611    /// 被移除的成员数量
1612    pub async fn zremrangebyscore(
1613        &self,
1614        key: impl Into<String>,
1615        min: f64,
1616        max: f64,
1617    ) -> Result<i64> {
1618        let mut cmd = redis::cmd("ZREMRANGEBYSCORE");
1619        cmd.arg(key.into()).arg(min).arg(max);
1620        let result = self.execute(&cmd).await?;
1621        result
1622            .as_i64()
1623            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1624    }
1625
1626    /// ZSCAN - 增量式迭代有序集合中的成员
1627    ///
1628    /// # 参数
1629    /// - `key`: 有序集合键
1630    /// - `cursor`: 游标(初始传 0)
1631    /// - `pattern`: 可选的匹配模式
1632    /// - `count`: 可选的每批返回数量提示
1633    ///
1634    /// # 返回
1635    /// - `Ok((i64, Vec<(String, f64)>))`: (下一游标, 成员-分数对列表)
1636    pub async fn zscan(
1637        &self,
1638        key: impl Into<String>,
1639        cursor: i64,
1640        pattern: Option<&str>,
1641        count: Option<i64>,
1642    ) -> Result<(i64, Vec<(String, f64)>)> {
1643        let mut cmd = redis::cmd("ZSCAN");
1644        cmd.arg(key.into()).arg(cursor);
1645        if let Some(p) = pattern {
1646            cmd.arg("MATCH").arg(p);
1647        }
1648        if let Some(c) = count {
1649            cmd.arg("COUNT").arg(c);
1650        }
1651        let result = self.execute(&cmd).await?;
1652        if let Some(arr) = result.as_array() {
1653            let next_cursor = arr
1654                .first()
1655                .and_then(|v| v.as_string())
1656                .and_then(|s| s.parse::<i64>().ok())
1657                .unwrap_or(0);
1658            if let Some(inner) = arr.get(1) {
1659                let pairs = parse_with_scores(inner);
1660                Ok((next_cursor, pairs))
1661            } else {
1662                Ok((next_cursor, vec![]))
1663            }
1664        } else {
1665            Ok((0, vec![]))
1666        }
1667    }
1668
1669    // ==================== 通用键操作 ====================
1670
1671    /// DEL - 删除一个或多个键
1672    ///
1673    /// # 返回
1674    /// 返回被删除的键数量
1675    pub async fn del(&self, keys: &[String]) -> Result<i64> {
1676        let mut cmd = redis::cmd("DEL");
1677        for key in keys {
1678            cmd.arg(key);
1679        }
1680        let result = self.execute(&cmd).await?;
1681        result
1682            .as_i64()
1683            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1684    }
1685
1686    /// EXISTS - 检查一个或多个键是否存在
1687    ///
1688    /// # 返回
1689    /// 返回存在的键数量
1690    pub async fn exists(&self, keys: &[String]) -> Result<i64> {
1691        let mut cmd = redis::cmd("EXISTS");
1692        for key in keys {
1693            cmd.arg(key);
1694        }
1695        let result = self.execute(&cmd).await?;
1696        result
1697            .as_i64()
1698            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1699    }
1700
1701    /// EXPIRE - 设置键的过期时间(秒)
1702    ///
1703    /// # 返回
1704    /// - `Ok(true)`: 设置成功
1705    /// - `Ok(false)`: 键不存在
1706    pub async fn expire(&self, key: impl Into<String>, seconds: i64) -> Result<bool> {
1707        let mut cmd = redis::cmd("EXPIRE");
1708        cmd.arg(key.into()).arg(seconds);
1709        let result = self.execute(&cmd).await?;
1710        Ok(result.as_i64() == Some(1))
1711    }
1712
1713    /// TTL - 获取键的剩余生存时间(秒)
1714    ///
1715    /// # 返回
1716    /// - 正数: 剩余秒数
1717    /// - -1: 键存在但没有过期时间
1718    /// - -2: 键不存在
1719    pub async fn ttl(&self, key: impl Into<String>) -> Result<i64> {
1720        let mut cmd = redis::cmd("TTL");
1721        cmd.arg(key.into());
1722        let result = self.execute(&cmd).await?;
1723        result
1724            .as_i64()
1725            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1726    }
1727
1728    /// PERSIST - 移除键的过期时间
1729    ///
1730    /// # 返回
1731    /// - `Ok(true)`: 移除成功
1732    /// - `Ok(false)`: 键不存在或没有过期时间
1733    pub async fn persist(&self, key: impl Into<String>) -> Result<bool> {
1734        let mut cmd = redis::cmd("PERSIST");
1735        cmd.arg(key.into());
1736        let result = self.execute(&cmd).await?;
1737        Ok(result.as_i64() == Some(1))
1738    }
1739
1740    /// KEYS - 查找所有匹配给定模式的键
1741    ///
1742    /// # 警告
1743    /// 此命令在生产环境中可能导致性能问题,请谨慎使用
1744    ///
1745    /// # 参数
1746    /// - `pattern`: 匹配模式(例如 "user:*")
1747    pub async fn keys(&self, pattern: impl Into<String>) -> Result<Vec<String>> {
1748        let mut cmd = redis::cmd("KEYS");
1749        cmd.arg(pattern.into());
1750        let result = self.execute(&cmd).await?;
1751        if let Some(arr) = result.as_array() {
1752            Ok(arr.iter().filter_map(|v| v.as_string()).collect())
1753        } else {
1754            Ok(vec![])
1755        }
1756    }
1757
1758    /// SCAN - 增量式迭代数据库中的所有键
1759    ///
1760    /// 生产安全的 KEYS 替代方案,不会阻塞 Redis 服务器。
1761    ///
1762    /// # 参数
1763    /// - `cursor`: 游标(初始传 0,使用返回的游标继续迭代)
1764    /// - `pattern`: 可选的键名匹配模式(如 `Some("user:*")`)
1765    /// - `count`: 可选的每批返回数量提示
1766    ///
1767    /// # 返回
1768    /// - `Ok((i64, Vec<String>))`: (下一游标, 本批键列表)
1769    /// - 游标为 0 表示完整迭代完成
1770    ///
1771    /// # 示例
1772    /// ```no_run
1773    /// # use yang_db::RedisClient;
1774    /// # async fn example() -> Result<(), yang_db::DbError> {
1775    /// # let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
1776    /// let mut cursor = 0i64;
1777    /// loop {
1778    ///     let (next, keys) = client.scan(cursor, Some("user:*"), Some(100)).await?;
1779    ///     // 处理 keys...
1780    ///     cursor = next;
1781    ///     if cursor == 0 { break; }
1782    /// }
1783    /// # Ok(())
1784    /// # }
1785    /// ```
1786    pub async fn scan(
1787        &self,
1788        cursor: i64,
1789        pattern: Option<&str>,
1790        count: Option<i64>,
1791    ) -> Result<(i64, Vec<String>)> {
1792        let mut cmd = redis::cmd("SCAN");
1793        cmd.arg(cursor);
1794        if let Some(p) = pattern {
1795            cmd.arg("MATCH").arg(p);
1796        }
1797        if let Some(c) = count {
1798            cmd.arg("COUNT").arg(c);
1799        }
1800        let result = self.execute(&cmd).await?;
1801        parse_scan_result(&result)
1802    }
1803
1804    /// PUBLISH - 向指定频道发布消息
1805    ///
1806    /// # 参数
1807    /// - `channel`: 频道名称
1808    /// - `message`: 消息内容
1809    ///
1810    /// # 返回
1811    /// 接收到此消息的订阅者数量
1812    pub async fn publish(
1813        &self,
1814        channel: impl Into<String>,
1815        message: impl Into<String>,
1816    ) -> Result<i64> {
1817        let mut cmd = redis::cmd("PUBLISH");
1818        cmd.arg(channel.into()).arg(message.into());
1819        let result = self.execute(&cmd).await?;
1820        result
1821            .as_i64()
1822            .ok_or_else(|| DbError::RedisTypeConversionError("无法转换为整数".to_string()))
1823    }
1824
1825    /// 健康检查 - 验证 Redis 连接是否正常
1826    ///
1827    /// 执行 PING 命令检查 Redis 服务可达性。连接异常时不抛错误,返回 Ok(false)。
1828    ///
1829    /// # 返回
1830    /// - `Ok(true)`: Redis 连接正常
1831    /// - `Ok(false)`: PING 响应异常
1832    /// - `Err(DbError)`: 无法获取连接
1833    pub async fn health_check(&self) -> Result<bool> {
1834        let cmd = redis::cmd("PING");
1835        match self.execute(&cmd).await {
1836            Ok(result) => Ok(!result.is_nil()),
1837            Err(_) => Ok(false),
1838        }
1839    }
1840
1841    /// 获取连接池当前状态
1842    ///
1843    /// # 返回
1844    /// PoolStatus 结构体,包含 max_size、size、available、waiting 统计信息
1845    pub fn pool_status(&self) -> PoolStatus {
1846        let s = self.pool.status();
1847        PoolStatus {
1848            max_size: s.max_size,
1849            size: s.size,
1850            available: s.available,
1851            waiting: s.waiting,
1852        }
1853    }
1854
1855    // ==================== Lua 脚本操作 ====================
1856
1857    /// 创建 Lua 脚本对象
1858    ///
1859    /// 返回 redis-rs 的原生 Script 类型,可用于执行 Lua 脚本。
1860    /// Script 会自动处理 EVALSHA 和 EVAL 的回退机制。
1861    ///
1862    /// # 参数
1863    /// - `code`: Lua 脚本代码
1864    ///
1865    /// # 返回
1866    /// redis::Script 对象
1867    ///
1868    /// # 示例
1869    /// ```no_run
1870    /// use yang_db::RedisClient;
1871    ///
1872    /// #[tokio::main]
1873    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
1874    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
1875    ///     
1876    ///     // 创建脚本:原子性地增加两个计数器
1877    ///     let script = client.script(
1878    ///         r#"
1879    ///         redis.call('INCR', KEYS[1])
1880    ///         redis.call('INCR', KEYS[2])
1881    ///         return redis.call('GET', KEYS[1])
1882    ///         "#
1883    ///     );
1884    ///     
1885    ///     // 执行脚本
1886    ///     let result: String = client.eval_script(
1887    ///         &script,
1888    ///         &["counter1".to_string(), "counter2".to_string()],
1889    ///         &[]
1890    ///     ).await?;
1891    ///     
1892    ///     println!("counter1 的值: {}", result);
1893    ///     Ok(())
1894    /// }
1895    /// ```
1896    pub fn script(&self, code: &str) -> redis::Script {
1897        redis::Script::new(code)
1898    }
1899
1900    /// 执行 Lua 脚本
1901    ///
1902    /// 使用 redis::Script 执行 Lua 脚本,自动处理 EVALSHA 和 EVAL 的回退。
1903    /// 脚本内的所有操作都是原子性的。
1904    ///
1905    /// # 参数
1906    /// - `script`: 脚本对象(通过 `script()` 方法创建)
1907    /// - `keys`: KEYS 参数列表(在脚本中通过 KEYS[1], KEYS[2] 访问)
1908    /// - `args`: ARGV 参数列表(在脚本中通过 ARGV[1], ARGV[2] 访问)
1909    ///
1910    /// # 返回
1911    /// - `Ok(T)`: 脚本执行成功,返回类型化结果
1912    /// - `Err(DbError)`: 脚本执行失败
1913    ///
1914    /// # 示例:原子性计数器增加
1915    /// ```no_run
1916    /// use yang_db::RedisClient;
1917    ///
1918    /// #[tokio::main]
1919    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
1920    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
1921    ///     
1922    ///     let script = client.script(
1923    ///         r#"
1924    ///         local current = redis.call('GET', KEYS[1])
1925    ///         if not current then
1926    ///             current = 0
1927    ///         end
1928    ///         local new_value = tonumber(current) + tonumber(ARGV[1])
1929    ///         redis.call('SET', KEYS[1], new_value)
1930    ///         return new_value
1931    ///         "#
1932    ///     );
1933    ///     
1934    ///     let result: i64 = client.eval_script(
1935    ///         &script,
1936    ///         &["my_counter".to_string()],
1937    ///         &["10".to_string()]
1938    ///     ).await?;
1939    ///     
1940    ///     println!("新值: {}", result);
1941    ///     Ok(())
1942    /// }
1943    /// ```
1944    ///
1945    /// # 示例:条件更新(乐观锁)
1946    /// ```no_run
1947    /// use yang_db::RedisClient;
1948    ///
1949    /// #[tokio::main]
1950    /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
1951    ///     let client = RedisClient::connect("redis://127.0.0.1:6379").await?;
1952    ///     
1953    ///     // 只有当余额足够时才扣减
1954    ///     let script = client.script(
1955    ///         r#"
1956    ///         local balance = tonumber(redis.call('GET', KEYS[1]) or 0)
1957    ///         local amount = tonumber(ARGV[1])
1958    ///         if balance >= amount then
1959    ///             redis.call('DECRBY', KEYS[1], amount)
1960    ///             return 1
1961    ///         else
1962    ///             return 0
1963    ///         end
1964    ///         "#
1965    ///     );
1966    ///     
1967    ///     let success: i64 = client.eval_script(
1968    ///         &script,
1969    ///         &["user:1000:balance".to_string()],
1970    ///         &["100".to_string()]
1971    ///     ).await?;
1972    ///     
1973    ///     if success == 1 {
1974    ///         println!("扣款成功");
1975    ///     } else {
1976    ///         println!("余额不足");
1977    ///     }
1978    ///     Ok(())
1979    /// }
1980    /// ```
1981    ///
1982    /// # 性能优化
1983    /// - 首次执行时,脚本会被发送到 Redis 服务器并缓存
1984    /// - 后续执行使用 EVALSHA 命令,只传输脚本的 SHA1 哈希值
1985    /// - 如果脚本不在缓存中,自动回退到 EVAL 命令
1986    pub async fn eval_script<T>(
1987        &self,
1988        script: &redis::Script,
1989        keys: &[String],
1990        args: &[String],
1991    ) -> Result<T>
1992    where
1993        T: redis::FromRedisValue,
1994    {
1995        let mut conn = self
1996            .pool
1997            .get()
1998            .await
1999            .map_err(|e| DbError::RedisPoolError(format!("获取连接失败: {}", e)))?;
2000
2001        // 准备脚本调用
2002        let mut invocation = script.prepare_invoke();
2003
2004        // 添加 KEYS 参数
2005        for key in keys {
2006            invocation.key(key);
2007        }
2008
2009        // 添加 ARGV 参数
2010        for arg in args {
2011            invocation.arg(arg);
2012        }
2013
2014        // 执行脚本
2015        invocation
2016            .invoke_async(&mut *conn)
2017            .await
2018            .map_err(|e| DbError::RedisCommandError(format!("Lua 脚本执行失败: {}", e)))
2019    }
2020}
2021
2022/// 解析 SCAN/SSCAN 结果
2023fn parse_scan_result(result: &RedisValue) -> crate::Result<(i64, Vec<String>)> {
2024    if let Some(arr) = result.as_array() {
2025        let cursor = arr
2026            .first()
2027            .and_then(|v| v.as_string())
2028            .and_then(|s| s.parse::<i64>().ok())
2029            .unwrap_or(0);
2030        let members = arr
2031            .get(1)
2032            .and_then(|v| v.as_array())
2033            .map(|a| a.iter().filter_map(|v| v.as_string()).collect())
2034            .unwrap_or_default();
2035        Ok((cursor, members))
2036    } else {
2037        Ok((0, vec![]))
2038    }
2039}
2040
2041/// 解析 WITHSCORES 结果(交替的成员/分数对)
2042fn parse_with_scores(result: &RedisValue) -> Vec<(String, f64)> {
2043    if let Some(arr) = result.as_array() {
2044        let mut pairs = Vec::new();
2045        let mut i = 0;
2046        while i + 1 < arr.len() {
2047            if let (Some(member), Some(score_str)) = (arr[i].as_string(), arr[i + 1].as_string()) {
2048                if let Ok(score) = score_str.parse::<f64>() {
2049                    pairs.push((member, score));
2050                }
2051            }
2052            i += 2;
2053        }
2054        pairs
2055    } else {
2056        vec![]
2057    }
2058}
2059
2060#[cfg(test)]
2061mod tests {
2062    use super::*;
2063
2064    #[test]
2065    fn test_redis_client_clone() {
2066        // 测试 Clone trait
2067        let config = RedisConfig::default();
2068        // 注意:这里只测试结构体的 Clone,不测试实际连接
2069        let _ = config.clone();
2070    }
2071
2072    #[test]
2073    fn test_redis_config_default() {
2074        let config = RedisConfig::default();
2075        assert_eq!(config.max_connections, 10);
2076        assert_eq!(config.connect_timeout, 5);
2077        assert_eq!(config.wait_timeout, 10);
2078        assert!(!config.enable_logging);
2079    }
2080
2081    #[test]
2082    fn test_redis_config_custom() {
2083        let config = RedisConfig::new(20, 10, 15, true);
2084        assert_eq!(config.max_connections, 20);
2085        assert_eq!(config.connect_timeout, 10);
2086        assert_eq!(config.wait_timeout, 15);
2087        assert!(config.enable_logging);
2088    }
2089
2090    /// 测试连接池配置构建逻辑
2091    ///
2092    /// 验证 PoolConfig 和 Timeouts 是否正确应用了 RedisConfig 参数
2093    #[test]
2094    fn test_pool_config_construction() {
2095        let redis_config = RedisConfig::new(25, 8, 12, true);
2096
2097        // 构建 PoolConfig
2098        let pool_config = PoolConfig {
2099            max_size: redis_config.max_connections,
2100            timeouts: Timeouts {
2101                wait: Some(Duration::from_secs(redis_config.wait_timeout)),
2102                create: Some(Duration::from_secs(redis_config.connect_timeout)),
2103                recycle: Some(Duration::from_secs(redis_config.connect_timeout)),
2104            },
2105            ..Default::default()
2106        };
2107
2108        // 验证配置参数
2109        assert_eq!(pool_config.max_size, 25, "最大连接数应为 25");
2110        assert_eq!(
2111            pool_config.timeouts.wait,
2112            Some(Duration::from_secs(12)),
2113            "等待超时应为 12 秒"
2114        );
2115        assert_eq!(
2116            pool_config.timeouts.create,
2117            Some(Duration::from_secs(8)),
2118            "创建超时应为 8 秒"
2119        );
2120        assert_eq!(
2121            pool_config.timeouts.recycle,
2122            Some(Duration::from_secs(8)),
2123            "回收超时应为 8 秒"
2124        );
2125    }
2126
2127    /// 测试默认配置的连接池构建
2128    #[test]
2129    fn test_default_pool_config_construction() {
2130        let redis_config = RedisConfig::default();
2131
2132        let pool_config = PoolConfig {
2133            max_size: redis_config.max_connections,
2134            timeouts: Timeouts {
2135                wait: Some(Duration::from_secs(redis_config.wait_timeout)),
2136                create: Some(Duration::from_secs(redis_config.connect_timeout)),
2137                recycle: Some(Duration::from_secs(redis_config.connect_timeout)),
2138            },
2139            ..Default::default()
2140        };
2141
2142        assert_eq!(pool_config.max_size, 10, "默认最大连接数应为 10");
2143        assert_eq!(
2144            pool_config.timeouts.wait,
2145            Some(Duration::from_secs(10)),
2146            "默认等待超时应为 10 秒"
2147        );
2148        assert_eq!(
2149            pool_config.timeouts.create,
2150            Some(Duration::from_secs(5)),
2151            "默认创建超时应为 5 秒"
2152        );
2153    }
2154
2155    #[test]
2156    fn test_pool_status_creation() {
2157        let status = PoolStatus {
2158            max_size: 10,
2159            size: 5,
2160            available: 3,
2161            waiting: 2,
2162        };
2163        assert_eq!(status.max_size, 10);
2164        assert_eq!(status.size, 5);
2165        assert_eq!(status.available, 3);
2166        assert_eq!(status.waiting, 2);
2167    }
2168
2169    #[test]
2170    fn test_parse_scan_result_valid() {
2171        let result = RedisValue::Array(vec![
2172            RedisValue::String("5".to_string()),
2173            RedisValue::Array(vec![
2174                RedisValue::String("key1".to_string()),
2175                RedisValue::String("key2".to_string()),
2176            ]),
2177        ]);
2178        let (cursor, keys) = parse_scan_result(&result).unwrap();
2179        assert_eq!(cursor, 5);
2180        assert_eq!(keys, vec!["key1".to_string(), "key2".to_string()]);
2181    }
2182
2183    #[test]
2184    fn test_parse_scan_result_empty() {
2185        let result = RedisValue::Array(vec![
2186            RedisValue::String("0".to_string()),
2187            RedisValue::Array(vec![]),
2188        ]);
2189        let (cursor, keys) = parse_scan_result(&result).unwrap();
2190        assert_eq!(cursor, 0);
2191        assert!(keys.is_empty());
2192    }
2193
2194    #[test]
2195    fn test_parse_with_scores_valid() {
2196        let result = RedisValue::Array(vec![
2197            RedisValue::String("alice".to_string()),
2198            RedisValue::String("100".to_string()),
2199            RedisValue::String("bob".to_string()),
2200            RedisValue::String("95.5".to_string()),
2201        ]);
2202        let pairs = parse_with_scores(&result);
2203        assert_eq!(pairs.len(), 2);
2204        assert_eq!(pairs[0], ("alice".to_string(), 100.0));
2205        assert_eq!(pairs[1], ("bob".to_string(), 95.5));
2206    }
2207
2208    #[test]
2209    fn test_parse_with_scores_empty() {
2210        let result = RedisValue::Array(vec![]);
2211        let pairs = parse_with_scores(&result);
2212        assert!(pairs.is_empty());
2213    }
2214}