Skip to main content

qail_redis/
driver.rs

1//! High-level Redis driver using unified QAIL AST.
2//!
3//! # Example
4//! ```ignore
5//! use qail_redis::RedisDriver;
6//! use qail_core::prelude::*;
7//!
8//! let mut driver = RedisDriver::connect("localhost", 6379).await?;
9//!
10//! // Using Qail AST
11//! driver.execute(&Qail::redis_set("key", b"value".to_vec())).await?;
12//! let val = driver.execute(&Qail::redis_get("key")).await?;
13//! ```
14
15use qail_core::ast::Qail;
16
17use crate::error::{RedisError, RedisResult};
18use crate::transport::Transport;
19use crate::value::Value;
20
21/// High-level Redis driver with connection management.
22pub struct RedisDriver {
23    transport: Transport,
24    resp3: bool,
25}
26
27impl RedisDriver {
28    /// Connect to Redis server.
29    pub async fn connect(host: &str, port: u16) -> RedisResult<Self> {
30        let mut transport = Transport::connect(host, port).await?;
31
32        // Upgrade to RESP3
33        let resp3 = transport.upgrade_to_resp3().await.unwrap_or(false);
34
35        Ok(Self { transport, resp3 })
36    }
37
38    /// Execute a Qail command.
39    pub async fn execute(&mut self, cmd: &Qail) -> RedisResult<Value> {
40        self.transport.execute(cmd).await
41    }
42
43    /// Check if connection is using RESP3.
44    pub fn is_resp3(&self) -> bool {
45        self.resp3
46    }
47
48    // ========== Convenience Methods ==========
49
50    /// GET key
51    pub async fn get(&mut self, key: &str) -> RedisResult<Option<Vec<u8>>> {
52        let cmd = Qail::redis_get(key);
53        match self.execute(&cmd).await? {
54            Value::Null => Ok(None),
55            Value::Bulk(data) => Ok(Some(data)),
56            Value::String(s) => Ok(Some(s.into_bytes())),
57            other => Err(RedisError::Protocol(format!(
58                "Unexpected GET response: {:?}",
59                other
60            ))),
61        }
62    }
63
64    /// GET key as string
65    pub async fn get_str(&mut self, key: &str) -> RedisResult<Option<String>> {
66        match self.get(key).await? {
67            Some(bytes) => Ok(Some(
68                String::from_utf8(bytes)
69                    .map_err(|e| RedisError::Protocol(e.to_string()))?,
70            )),
71            None => Ok(None),
72        }
73    }
74
75    /// SET key value
76    pub async fn set(&mut self, key: &str, value: &[u8]) -> RedisResult<()> {
77        let cmd = Qail::redis_set(key, value.to_vec());
78        self.execute(&cmd).await?;
79        Ok(())
80    }
81
82    /// SET key value EX seconds
83    pub async fn set_ex(&mut self, key: &str, value: &[u8], seconds: i64) -> RedisResult<()> {
84        use crate::cmd::RedisExt;
85        let cmd = Qail::redis_set(key, value.to_vec()).redis_ex(seconds);
86        self.execute(&cmd).await?;
87        Ok(())
88    }
89
90    /// DEL key
91    pub async fn del(&mut self, key: &str) -> RedisResult<i64> {
92        let cmd = Qail::redis_del(key);
93        match self.execute(&cmd).await? {
94            Value::Integer(n) => Ok(n),
95            other => Err(RedisError::Protocol(format!(
96                "Unexpected DEL response: {:?}",
97                other
98            ))),
99        }
100    }
101
102    /// INCR key
103    pub async fn incr(&mut self, key: &str) -> RedisResult<i64> {
104        let cmd = Qail::redis_incr(key);
105        match self.execute(&cmd).await? {
106            Value::Integer(n) => Ok(n),
107            other => Err(RedisError::Protocol(format!(
108                "Unexpected INCR response: {:?}",
109                other
110            ))),
111        }
112    }
113
114    /// TTL key
115    pub async fn ttl(&mut self, key: &str) -> RedisResult<i64> {
116        let cmd = Qail::redis_ttl(key);
117        match self.execute(&cmd).await? {
118            Value::Integer(n) => Ok(n),
119            other => Err(RedisError::Protocol(format!(
120                "Unexpected TTL response: {:?}",
121                other
122            ))),
123        }
124    }
125
126    /// EXPIRE key seconds
127    pub async fn expire(&mut self, key: &str, seconds: i64) -> RedisResult<bool> {
128        let cmd = Qail::redis_expire(key, seconds);
129        match self.execute(&cmd).await? {
130            Value::Integer(1) => Ok(true),
131            Value::Integer(0) => Ok(false),
132            other => Err(RedisError::Protocol(format!(
133                "Unexpected EXPIRE response: {:?}",
134                other
135            ))),
136        }
137    }
138
139    /// EXISTS key
140    pub async fn exists(&mut self, key: &str) -> RedisResult<bool> {
141        let cmd = Qail::redis_exists(key);
142        match self.execute(&cmd).await? {
143            Value::Integer(n) => Ok(n > 0),
144            other => Err(RedisError::Protocol(format!(
145                "Unexpected EXISTS response: {:?}",
146                other
147            ))),
148        }
149    }
150
151    /// PING
152    pub async fn ping(&mut self) -> RedisResult<bool> {
153        let cmd = Qail::redis_ping();
154        match self.execute(&cmd).await? {
155            Value::String(s) if s == "PONG" => Ok(true),
156            Value::Bulk(b) if b == b"PONG" => Ok(true),
157            _ => Ok(false),
158        }
159    }
160}