Skip to main content

synap_sdk/
list.rs

1//! List data structure operations
2
3use crate::client::SynapClient;
4use crate::error::Result;
5use serde_json::json;
6
7/// List data structure interface (Redis-compatible)
8///
9/// List is a doubly-linked list with O(1) push/pop at both ends.
10#[derive(Clone)]
11pub struct ListManager {
12    client: SynapClient,
13}
14
15impl ListManager {
16    /// Create a new List manager interface
17    pub(crate) fn new(client: SynapClient) -> Self {
18        Self { client }
19    }
20
21    /// Push elements to left (head) of list
22    pub async fn lpush<K>(&self, key: K, values: Vec<String>) -> Result<usize>
23    where
24        K: AsRef<str>,
25    {
26        let payload = json!({
27            "key": key.as_ref(),
28            "values": values,
29        });
30
31        let response = self.client.send_command("list.lpush", payload).await?;
32        Ok(response.get("length").and_then(|v| v.as_u64()).unwrap_or(0) as usize)
33    }
34
35    /// Push elements to right (tail) of list
36    pub async fn rpush<K>(&self, key: K, values: Vec<String>) -> Result<usize>
37    where
38        K: AsRef<str>,
39    {
40        let payload = json!({
41            "key": key.as_ref(),
42            "values": values,
43        });
44
45        let response = self.client.send_command("list.rpush", payload).await?;
46        Ok(response.get("length").and_then(|v| v.as_u64()).unwrap_or(0) as usize)
47    }
48
49    /// Pop elements from left (head) of list
50    ///
51    /// # Arguments
52    /// * `key` - The list key
53    /// * `count` - Number of elements to pop (optional, defaults to 1)
54    pub async fn lpop<K>(&self, key: K, count: Option<usize>) -> Result<Vec<String>>
55    where
56        K: AsRef<str>,
57    {
58        let mut payload = json!({
59            "key": key.as_ref(),
60        });
61
62        if let Some(c) = count {
63            payload["count"] = json!(c);
64        }
65
66        let response = self.client.send_command("list.lpop", payload).await?;
67        let values = response
68            .get("values")
69            .and_then(|v| serde_json::from_value(v.clone()).ok())
70            .unwrap_or_default();
71
72        Ok(values)
73    }
74
75    /// Pop elements from right (tail) of list
76    ///
77    /// # Arguments
78    /// * `key` - The list key
79    /// * `count` - Number of elements to pop (optional, defaults to 1)
80    pub async fn rpop<K>(&self, key: K, count: Option<usize>) -> Result<Vec<String>>
81    where
82        K: AsRef<str>,
83    {
84        let mut payload = json!({
85            "key": key.as_ref(),
86        });
87
88        if let Some(c) = count {
89            payload["count"] = json!(c);
90        }
91
92        let response = self.client.send_command("list.rpop", payload).await?;
93        let values = response
94            .get("values")
95            .and_then(|v| serde_json::from_value(v.clone()).ok())
96            .unwrap_or_default();
97
98        Ok(values)
99    }
100
101    /// Get range of elements from list
102    pub async fn range<K>(&self, key: K, start: i64, stop: i64) -> Result<Vec<String>>
103    where
104        K: AsRef<str>,
105    {
106        let payload = json!({
107            "key": key.as_ref(),
108            "start": start,
109            "stop": stop,
110        });
111
112        let response = self.client.send_command("list.range", payload).await?;
113        let values = response
114            .get("values")
115            .and_then(|v| serde_json::from_value(v.clone()).ok())
116            .unwrap_or_default();
117
118        Ok(values)
119    }
120
121    /// Get list length
122    pub async fn len<K>(&self, key: K) -> Result<usize>
123    where
124        K: AsRef<str>,
125    {
126        let payload = json!({"key": key.as_ref()});
127        let response = self.client.send_command("list.len", payload).await?;
128        Ok(response.get("length").and_then(|v| v.as_u64()).unwrap_or(0) as usize)
129    }
130
131    /// Get element at index
132    pub async fn index<K>(&self, key: K, index: i64) -> Result<Option<String>>
133    where
134        K: AsRef<str>,
135    {
136        let payload = json!({
137            "key": key.as_ref(),
138            "index": index,
139        });
140
141        let response = self.client.send_command("list.index", payload).await?;
142        Ok(response
143            .get("value")
144            .and_then(|v| v.as_str())
145            .map(String::from))
146    }
147
148    /// Set element at index
149    pub async fn set<K>(&self, key: K, index: i64, value: String) -> Result<bool>
150    where
151        K: AsRef<str>,
152    {
153        let payload = json!({
154            "key": key.as_ref(),
155            "index": index,
156            "value": value,
157        });
158
159        let response = self.client.send_command("list.set", payload).await?;
160        Ok(response
161            .get("success")
162            .and_then(|v| v.as_bool())
163            .unwrap_or(false))
164    }
165
166    /// Trim list to specified range
167    pub async fn trim<K>(&self, key: K, start: i64, stop: i64) -> Result<bool>
168    where
169        K: AsRef<str>,
170    {
171        let payload = json!({
172            "key": key.as_ref(),
173            "start": start,
174            "stop": stop,
175        });
176
177        let response = self.client.send_command("list.trim", payload).await?;
178        Ok(response
179            .get("success")
180            .and_then(|v| v.as_bool())
181            .unwrap_or(false))
182    }
183
184    /// Remove elements from list
185    pub async fn rem<K>(&self, key: K, count: i64, value: String) -> Result<usize>
186    where
187        K: AsRef<str>,
188    {
189        let payload = json!({
190            "key": key.as_ref(),
191            "count": count,
192            "value": value,
193        });
194
195        let response = self.client.send_command("list.rem", payload).await?;
196        Ok(response
197            .get("removed")
198            .and_then(|v| v.as_u64())
199            .unwrap_or(0) as usize)
200    }
201
202    /// Insert element before/after pivot
203    pub async fn insert<K>(
204        &self,
205        key: K,
206        position: &str,
207        pivot: String,
208        value: String,
209    ) -> Result<i64>
210    where
211        K: AsRef<str>,
212    {
213        let payload = json!({
214            "key": key.as_ref(),
215            "position": position.to_lowercase(),
216            "pivot": pivot,
217            "value": value,
218        });
219
220        let response = self.client.send_command("list.insert", payload).await?;
221        Ok(response
222            .get("length")
223            .and_then(|v| v.as_i64())
224            .unwrap_or(-1))
225    }
226
227    /// Pop from source and push to destination (atomic)
228    pub async fn rpoplpush<S, D>(&self, source: S, destination: D) -> Result<Option<String>>
229    where
230        S: AsRef<str>,
231        D: AsRef<str>,
232    {
233        let payload = json!({
234            "source": source.as_ref(),
235            "destination": destination.as_ref(),
236        });
237
238        let response = self.client.send_command("list.rpoplpush", payload).await?;
239        Ok(response
240            .get("value")
241            .and_then(|v| v.as_str())
242            .map(String::from))
243    }
244
245    /// Find first position of element
246    pub async fn pos<K>(&self, key: K, element: String, rank: i64) -> Result<Option<i64>>
247    where
248        K: AsRef<str>,
249    {
250        let payload = json!({
251            "key": key.as_ref(),
252            "element": element,
253            "rank": rank,
254        });
255
256        let response = self.client.send_command("list.pos", payload).await?;
257        Ok(response.get("position").and_then(|v| v.as_i64()))
258    }
259
260    /// Push to left only if list exists
261    pub async fn lpushx<K>(&self, key: K, values: Vec<String>) -> Result<usize>
262    where
263        K: AsRef<str>,
264    {
265        let payload = json!({
266            "key": key.as_ref(),
267            "values": values,
268        });
269
270        let response = self.client.send_command("list.lpushx", payload).await?;
271        Ok(response.get("length").and_then(|v| v.as_u64()).unwrap_or(0) as usize)
272    }
273
274    /// Push to right only if list exists
275    pub async fn rpushx<K>(&self, key: K, values: Vec<String>) -> Result<usize>
276    where
277        K: AsRef<str>,
278    {
279        let payload = json!({
280            "key": key.as_ref(),
281            "values": values,
282        });
283
284        let response = self.client.send_command("list.rpushx", payload).await?;
285        Ok(response.get("length").and_then(|v| v.as_u64()).unwrap_or(0) as usize)
286    }
287}