embedded_redis/commands/hgetall.rs
1//! Abstraction of HGETALL command.
2//!
3//! For general information about this command, see the [Redis documentation](<https://redis.io/commands/hgetall/>).
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::hgetall::HashGetAllCommand;
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.hset("test_all_hash", "color", "green").unwrap().wait().unwrap();
21//! client.hset("test_all_hash", "material", "wood").unwrap().wait().unwrap();
22//!
23//! let command = HashGetAllCommand::new("test_all_hash");
24//! let response = client.send(command).unwrap().wait().unwrap().unwrap();
25//!
26//! assert_eq!("green", response.get_str("color").unwrap());
27//! assert_eq!("wood", response.get_str("material").unwrap());
28//! ```
29//!
30//! # Missing key or field
31//! In case key or field is missing. [None] is returned.
32//! ```
33//!# use core::str::FromStr;
34//!# use core::net::SocketAddr;
35//!# use std_embedded_nal::Stack;
36//!# use std_embedded_time::StandardClock;
37//!# use embedded_redis::commands::builder::CommandBuilder;
38//!# use embedded_redis::commands::hgetall::HashGetAllCommand;
39//!# use embedded_redis::network::ConnectionHandler;
40//!#
41//!# let mut 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(&mut stack, Some(&clock)).unwrap();
46//!#
47//! let command = HashGetAllCommand::new("not_existing");
48//! let response = client.send(command).unwrap().wait().unwrap();
49//!
50//! assert!(response.is_none())
51//! ```
52//!
53//! # Shorthand
54//! [Client](Client#method.hgetall) provides a shorthand method for this command.
55//! ```
56//!# use core::str::FromStr;
57//!# use bytes::Bytes;
58//!# use core::net::SocketAddr;
59//!# use std_embedded_nal::Stack;
60//!# use std_embedded_time::StandardClock;
61//!# use embedded_redis::commands::hset::HashSetCommand;
62//!# use embedded_redis::commands::set::SetCommand;
63//!# use embedded_redis::network::ConnectionHandler;
64//!#
65//!# let mut stack = Stack::default();
66//!# let clock = StandardClock::default();
67//!#
68//!# let mut connection_handler = ConnectionHandler::resp2(SocketAddr::from_str("127.0.0.1:6379").unwrap());
69//!# let client = connection_handler.connect(&mut stack, Some(&clock)).unwrap();
70//!#
71//!# let _ = client.send(HashSetCommand::new("multi_hash_key", "first_field", "green")).unwrap().wait();
72//!# let _ = client.send(HashSetCommand::new("multi_hash_key", "second_field", "wood")).unwrap().wait();
73//!#
74//! // Using &str arguments
75//! let response = client.hgetall("multi_hash_key").unwrap().wait().unwrap().unwrap();
76//! assert_eq!("green", response.get_str("first_field").unwrap());
77//! assert_eq!("wood", response.get_str("second_field").unwrap());
78//!
79//! // Using String arguments
80//! let _ = client.hgetall("multi_hash_key".to_string());
81//!
82//! // Using Bytes arguments
83//! let _ = client.hgetall(Bytes::from_static(b"multi_hash_key"));
84//! ```
85use crate::commands::auth::AuthCommand;
86use crate::commands::builder::{CommandBuilder, ToBytesMap};
87use crate::commands::hello::HelloCommand;
88use crate::commands::{Command, ResponseTypeError};
89use crate::network::protocol::Protocol;
90use crate::network::{Client, CommandErrors, Future};
91use alloc::collections::BTreeMap;
92use bytes::Bytes;
93use embedded_nal::TcpClientStack;
94use embedded_time::Clock;
95
96/// Abstraction for HGETALL command
97pub struct HashGetAllCommand {
98 /// Hash key
99 key: Bytes,
100}
101
102impl HashGetAllCommand {
103 pub fn new<K>(key: K) -> Self
104 where
105 Bytes: From<K>,
106 {
107 Self { key: key.into() }
108 }
109}
110
111#[derive(Debug)]
112pub struct HashResponse {
113 /// Field/Value map
114 inner: BTreeMap<Bytes, Bytes>,
115}
116
117impl HashResponse {
118 pub fn new(inner: BTreeMap<Bytes, Bytes>) -> Self {
119 Self { inner }
120 }
121
122 /// Extracts inner map
123 #[allow(clippy::wrong_self_convention)]
124 pub fn to_map(self) -> BTreeMap<Bytes, Bytes> {
125 self.inner
126 }
127
128 /// Returns the given field as &str. Returns None in case field is missing or value has invalid UTF8 encoding
129 pub fn get_str<F>(&self, field: F) -> Option<&str>
130 where
131 Bytes: From<F>,
132 {
133 let field: Bytes = field.into();
134
135 match self.inner.get(&field) {
136 None => None,
137 Some(value) => match core::str::from_utf8(value) {
138 Ok(value) => Some(value),
139 Err(_) => None,
140 },
141 }
142 }
143}
144
145impl<F> Command<F> for HashGetAllCommand
146where
147 F: From<CommandBuilder> + ToBytesMap,
148{
149 type Response = Option<HashResponse>;
150
151 fn encode(&self) -> F {
152 CommandBuilder::new("HGETALL").arg(&self.key).into()
153 }
154
155 fn eval_response(&self, frame: F) -> Result<Self::Response, ResponseTypeError> {
156 let map = frame.to_map();
157
158 if map.is_none() {
159 return Err(ResponseTypeError {});
160 }
161
162 if map.as_ref().unwrap().is_empty() {
163 return Ok(None);
164 }
165
166 Ok(Some(HashResponse { inner: map.unwrap() }))
167 }
168}
169
170impl<'a, N: TcpClientStack, C: Clock, P: Protocol> Client<'a, N, C, P>
171where
172 AuthCommand: Command<<P as Protocol>::FrameType>,
173 HelloCommand: Command<<P as Protocol>::FrameType>,
174{
175 /// Shorthand for [HashGetAllCommand]
176 pub fn hgetall<K>(&'a self, key: K) -> Result<Future<'a, N, C, P, HashGetAllCommand>, CommandErrors>
177 where
178 <P as Protocol>::FrameType: ToBytesMap,
179 <P as Protocol>::FrameType: From<CommandBuilder>,
180 Bytes: From<K>,
181 {
182 self.send(HashGetAllCommand::new(key))
183 }
184}