spin_sdk/redis.rs
1//! Redis storage and message publishing.
2//!
3//! To receive Redis messages, use the Redis trigger.
4//!
5//! # Examples
6//!
7//! Get a value from the Redis database.
8//!
9//! ```no_run
10//! use spin_sdk::redis::Connection;
11//!
12//! # async fn run() -> anyhow::Result<()> {
13//! let conn = Connection::open("redis://127.0.0.1:6379").await?;
14//! let payload = conn.get("archimedes-data").await?;
15//! if let Some(data) = payload {
16//! println!("{}", String::from_utf8_lossy(&data));
17//! }
18//! # Ok(())
19//! # }
20//! ```
21//!
22//! See the [`Connection`] type for further examples.
23
24#[doc(hidden)]
25/// Module containing wit bindgen generated code.
26///
27/// This is only meant for internal consumption.
28pub mod wit {
29 #![allow(missing_docs)]
30 use crate::wit_bindgen;
31
32 wit_bindgen::generate!({
33 runtime_path: "crate::wit_bindgen::rt",
34 world: "spin-sdk-redis",
35 path: "wit",
36 generate_all,
37 });
38
39 pub use spin::redis::redis;
40}
41
42use std::hash::{Hash, Hasher};
43
44/// An open connection to a Redis server.
45///
46/// # Examples
47///
48/// Get a value from the Redis database.
49///
50/// ```no_run
51/// use spin_sdk::redis::Connection;
52///
53/// # async fn run() -> anyhow::Result<()> {
54/// let conn = Connection::open("redis://127.0.0.1:6379").await?;
55/// let payload = conn.get("archimedes-data").await?;
56/// if let Some(data) = payload {
57/// println!("{}", String::from_utf8_lossy(&data));
58/// }
59/// # Ok(())
60/// # }
61/// ```
62///
63/// Set a value in the Redis database.
64///
65/// ```no_run
66/// use spin_sdk::redis::Connection;
67///
68/// # async fn run() -> anyhow::Result<()> {
69/// let conn = Connection::open("redis://127.0.0.1:6379").await?;
70/// let payload = "Eureka!".to_owned().into_bytes();
71/// conn.set("archimedes-data", &payload).await?;
72/// # Ok(())
73/// # }
74/// ```
75///
76/// Delete a value from the Redis database.
77///
78/// ```no_run
79/// use spin_sdk::redis::Connection;
80///
81/// # async fn run() -> anyhow::Result<()> {
82/// let conn = Connection::open("redis://127.0.0.1:6379").await?;
83/// conn.del(&["archimedes-data"]).await?;
84/// # Ok(())
85/// # }
86/// ```
87///
88/// Publish a message to a Redis channel.
89///
90/// ```no_run
91/// use spin_sdk::redis::Connection;
92///
93/// # async fn run() -> anyhow::Result<()> {
94/// let conn = Connection::open("redis://127.0.0.1:6379").await?;
95///
96/// let payload = b"cute pet picture".to_vec();
97///
98/// conn.publish("pet-pictures", &payload).await?;
99/// # Ok(())
100/// # }
101/// ```
102pub struct Connection(wit::redis::Connection);
103
104pub use wit::redis::{Error, Payload, RedisParameter, RedisResult};
105
106impl Connection {
107 /// Open a connection to the Redis instance at `address`.
108 pub async fn open(address: impl AsRef<str>) -> Result<Self, Error> {
109 wit::redis::Connection::open(address.as_ref().to_string())
110 .await
111 .map(Connection)
112 }
113
114 /// Publish a Redis message to the specified channel.
115 pub async fn publish(
116 &self,
117 channel: impl AsRef<str>,
118 payload: impl AsRef<[u8]>,
119 ) -> Result<(), Error> {
120 self.0
121 .publish(channel.as_ref().to_string(), payload.as_ref().to_vec())
122 .await
123 }
124
125 /// Get the value of a key.
126 pub async fn get(&self, key: impl AsRef<str>) -> Result<Option<Payload>, Error> {
127 self.0.get(key.as_ref().to_string()).await
128 }
129
130 /// Set key to value.
131 ///
132 /// If key already holds a value, it is overwritten.
133 pub async fn set(&self, key: impl AsRef<str>, value: impl AsRef<[u8]>) -> Result<(), Error> {
134 self.0
135 .set(key.as_ref().to_string(), value.as_ref().to_vec())
136 .await
137 }
138
139 /// Increments the number stored at key by one.
140 ///
141 /// If the key does not exist, it is set to 0 before performing the operation.
142 /// An `error::type-error` is returned if the key contains a value of the wrong type
143 /// or contains a string that can not be represented as integer.
144 pub async fn incr(&self, key: impl AsRef<str>) -> Result<i64, Error> {
145 self.0.incr(key.as_ref().to_string()).await
146 }
147
148 /// Removes the specified keys.
149 ///
150 /// A key is ignored if it does not exist. Returns the number of keys deleted.
151 pub async fn del<Key: AsRef<str>>(
152 &self,
153 keys: impl IntoIterator<Item = Key>,
154 ) -> Result<u32, Error> {
155 self.0
156 .del(
157 keys.into_iter()
158 .map(|key| key.as_ref().to_string())
159 .collect(),
160 )
161 .await
162 }
163
164 /// Add the specified `values` to the set named `key`, returning the number of newly-added values.
165 pub async fn sadd<Val: AsRef<str>>(
166 &self,
167 key: impl AsRef<str>,
168 values: impl IntoIterator<Item = Val>,
169 ) -> Result<u32, Error> {
170 let values = values
171 .into_iter()
172 .map(|key| key.as_ref().to_string())
173 .collect();
174
175 self.0.sadd(key.as_ref().to_string(), values).await
176 }
177
178 /// Retrieve the contents of the set named `key`.
179 pub async fn smembers(&self, key: impl AsRef<str>) -> Result<Vec<String>, Error> {
180 self.0.smembers(key.as_ref().to_string()).await
181 }
182
183 /// Remove the specified `values` from the set named `key`, returning the number of newly-removed values.
184 pub async fn srem<Val: AsRef<str>>(
185 &self,
186 key: impl AsRef<str>,
187 values: impl IntoIterator<Item = Val>,
188 ) -> Result<u32, Error> {
189 let values = values
190 .into_iter()
191 .map(|key| key.as_ref().to_string())
192 .collect();
193
194 self.0.srem(key.as_ref().to_string(), values).await
195 }
196
197 /// Execute an arbitrary Redis command and receive the result.
198 pub async fn execute(
199 &self,
200 command: impl AsRef<str>,
201 arguments: impl IntoIterator<Item = RedisParameter>,
202 ) -> Result<Vec<RedisResult>, Error> {
203 self.0
204 .execute(
205 command.as_ref().to_string(),
206 arguments.into_iter().collect(),
207 )
208 .await
209 }
210}
211
212impl PartialEq for RedisResult {
213 fn eq(&self, other: &Self) -> bool {
214 use RedisResult::*;
215 match (self, other) {
216 (Nil, Nil) => true,
217 (Status(a), Status(b)) => a == b,
218 (Int64(a), Int64(b)) => a == b,
219 (Binary(a), Binary(b)) => a == b,
220 _ => false,
221 }
222 }
223}
224
225impl Eq for RedisResult {}
226
227impl Hash for RedisResult {
228 fn hash<H: Hasher>(&self, state: &mut H) {
229 use RedisResult::*;
230
231 match self {
232 Nil => (),
233 Status(s) => s.hash(state),
234 Int64(v) => v.hash(state),
235 Binary(v) => v.hash(state),
236 }
237 }
238}