Skip to main content

embedded_redis/commands/
hset.rs

1//! Abstraction of HSET command.
2//!
3//! For general information about this command, see the [Redis documentation](<https://redis.io/commands/hset/>).
4//!
5//! # Using command object
6//! ```
7//! # async_std::task::block_on(async {
8//!# use core::str::FromStr;
9//!# use core::net::SocketAddr;
10//!# use std_embedded_nal_async::Stack;
11//!# use std_embedded_time::StandardClock;
12//!# use embedded_redis::commands::builder::CommandBuilder;
13//! use embedded_redis::commands::hset::HashSetCommand;
14//!# use embedded_redis::network::ConnectionHandler;
15//!#
16//! let stack = Stack::default();
17//! let clock = StandardClock::default();
18//!
19//! let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
20//! let client = connection_handler.connect(&stack, Some(&clock)).await.unwrap();
21//!# client.send(CommandBuilder::new("DEL").arg_static("my_hash").to_command()).await.unwrap();
22//!
23//! let command = HashSetCommand::new("my_hash", "color", "green");
24//! let response = client.send(command).await.unwrap();
25//!
26//! // Returns the number of added fields
27//! assert_eq!(1, response)
28//! # });
29//! ```
30//! # Setting multiple fields at once
31//! ```
32//! # async_std::task::block_on(async {
33//!# use core::str::FromStr;
34//!# use core::net::SocketAddr;
35//!# use std_embedded_nal_async::Stack;
36//!# use std_embedded_time::StandardClock;
37//!# use embedded_redis::commands::builder::CommandBuilder;
38//!# use embedded_redis::commands::hset::HashSetCommand;
39//!# use embedded_redis::network::ConnectionHandler;
40//!#
41//!# let stack = Stack::default();
42//!# let clock = StandardClock::default();
43//!#
44//!# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
45//!# let client = connection_handler.connect(&stack, Some(&clock)).await.unwrap();
46//!# client.send(CommandBuilder::new("DEL").arg_static("my_hash").to_command()).await.unwrap();
47//!#
48//! let command = HashSetCommand::multiple("my_hash".into(), [
49//!     ("color".into(), "green".into()),
50//!     ("material".into(), "stone".into())
51//! ]);
52//! let response = client.send(command).await.unwrap();
53//!
54//! // Returns the number of added fields
55//! assert_eq!(2, response)
56//! # });
57//! ```
58//! # Shorthand
59//! [Client](Client#method.hset) provides a shorthand method for this command.
60//! ```
61//! # async_std::task::block_on(async {
62//!# use core::str::FromStr;
63//!# use bytes::Bytes;
64//!# use core::net::SocketAddr;
65//!# use std_embedded_nal_async::Stack;
66//!# use std_embedded_time::StandardClock;
67//!# use embedded_redis::network::ConnectionHandler;
68//!#
69//!# let stack = Stack::default();
70//!# let clock = StandardClock::default();
71//!#
72//!# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
73//!# let client = connection_handler.connect(&stack, Some(&clock)).await.unwrap();
74//! // Using &str arguments
75//! let _ = client.hset("hash", "field", "value").await;
76//!
77//! // Using String arguments
78//! let _ = client.hset("hash".to_string(), "field".to_string(), "value".to_string()).await;
79//!
80//! // Using Bytes arguments
81//! let _ = client.hset(Bytes::from_static(b"hash"), Bytes::from_static(b"field"), Bytes::from_static(b"value")).await;
82//! # });
83//! ```
84use crate::commands::auth::AuthCommand;
85use crate::commands::builder::{CommandBuilder, ToInteger};
86use crate::commands::hello::HelloCommand;
87use crate::commands::{Command, ResponseTypeError};
88use crate::network::protocol::Protocol;
89use crate::network::{Client, CommandErrors};
90use bytes::Bytes;
91use embedded_io_async::{Read, Write};
92use embedded_time::Clock;
93
94/// Abstraction of HSET command
95pub struct HashSetCommand<const N: usize> {
96    /// Hash key
97    key: Bytes,
98
99    /// Field/Value paris
100    fields: [(Bytes, Bytes); N],
101}
102
103impl HashSetCommand<1> {
104    pub fn new<K, F, V>(key: K, field: F, value: V) -> Self
105    where
106        Bytes: From<K>,
107        Bytes: From<F>,
108        Bytes: From<V>,
109    {
110        Self {
111            key: key.into(),
112            fields: [(field.into(), value.into())],
113        }
114    }
115}
116
117impl<const N: usize> HashSetCommand<N> {
118    /// Constructs a new command with multiple field/value paris
119    pub fn multiple(key: Bytes, fields: [(Bytes, Bytes); N]) -> Self {
120        Self { key, fields }
121    }
122}
123
124impl<F: From<CommandBuilder> + ToInteger, const N: usize> Command<F> for HashSetCommand<N> {
125    type Response = i64;
126
127    fn encode(&self) -> F {
128        let mut builder = CommandBuilder::new("HSET").arg(&self.key);
129
130        for (field, value) in &self.fields {
131            builder = builder.arg(field).arg(value);
132        }
133
134        builder.into()
135    }
136
137    fn eval_response(&self, frame: F) -> Result<Self::Response, ResponseTypeError> {
138        frame.to_integer().ok_or(ResponseTypeError {})
139    }
140}
141
142impl<'a, T: Read + Write, C: Clock, P: Protocol> Client<'a, T, C, P>
143where
144    AuthCommand: Command<<P as Protocol>::FrameType>,
145    HelloCommand: Command<<P as Protocol>::FrameType>,
146{
147    /// Shorthand for [HashSetCommand]
148    /// For setting multiple fields, use [HashSetCommand] directly instead
149    pub async fn hset<K, F, V>(&self, key: K, field: F, value: V) -> Result<i64, CommandErrors>
150    where
151        Bytes: From<K>,
152        Bytes: From<F>,
153        Bytes: From<V>,
154        <P as Protocol>::FrameType: ToInteger,
155        <P as Protocol>::FrameType: From<CommandBuilder>,
156    {
157        self.send(HashSetCommand::new(key, field, value)).await
158    }
159}