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