Skip to main content

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}