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