embedded_redis/commands/
builder.rs1use crate::commands::custom::CustomCommand;
34use alloc::collections::BTreeMap;
35use alloc::string::{String, ToString};
36use alloc::vec;
37use alloc::vec::Vec;
38use bytes::Bytes;
39use redis_protocol::resp2::types::{BytesFrame as Resp2Frame, Resp2Frame as _};
40use redis_protocol::resp3::types::{BytesFrame as Resp3Frame, Resp3Frame as _};
41
42#[derive(Clone, Default)]
44pub struct CommandBuilder {
45 pub(crate) elements: Vec<Bytes>,
46}
47
48impl CommandBuilder {
49 pub fn new(keyword: &'static str) -> Self {
50 CommandBuilder {
51 elements: vec![Bytes::from_static(keyword.as_bytes())],
52 }
53 }
54
55 pub fn to_command(self) -> CustomCommand {
57 self.into()
58 }
59
60 pub fn arg_static(mut self, arg: &'static str) -> Self {
62 self.elements.push(Bytes::from_static(arg.as_bytes()));
63 self
64 }
65
66 pub fn arg_static_option(mut self, arg: Option<&'static str>) -> Self {
68 if let Some(arg_str) = arg {
69 self.elements.push(Bytes::from_static(arg_str.as_bytes()));
70 }
71 self
72 }
73
74 pub fn arg_uint(mut self, arg: usize) -> Self {
76 self.elements.push(Bytes::from(arg.to_string()));
77 self
78 }
79
80 pub fn arg(mut self, arg: &Bytes) -> Self {
83 self.elements.push(arg.clone());
84 self
85 }
86
87 pub fn arg_option(mut self, arg: Option<&Bytes>) -> Self {
89 if let Some(inner) = arg {
90 self.elements.push(inner.clone());
91 }
92 self
93 }
94}
95
96impl From<CommandBuilder> for Resp2Frame {
97 fn from(builder: CommandBuilder) -> Self {
98 let mut frames = Vec::with_capacity(builder.elements.len());
99 for byte in builder.elements {
100 frames.push(Resp2Frame::BulkString(byte));
101 }
102
103 Resp2Frame::Array(frames)
104 }
105}
106
107impl From<CommandBuilder> for Resp3Frame {
108 fn from(builder: CommandBuilder) -> Self {
109 let mut frames = Vec::with_capacity(builder.elements.len());
110 for byte in builder.elements {
111 frames.push(Resp3Frame::BlobString {
112 data: byte,
113 attributes: None,
114 });
115 }
116
117 Resp3Frame::Array {
118 data: frames,
119 attributes: None,
120 }
121 }
122}
123
124impl From<CommandBuilder> for CustomCommand {
125 fn from(builder: CommandBuilder) -> Self {
126 CustomCommand::new(builder)
127 }
128}
129
130pub trait ToStringOption {
132 fn to_string_option(&self) -> Option<String>;
133}
134
135impl ToStringOption for Resp2Frame {
136 fn to_string_option(&self) -> Option<String> {
137 self.to_string()
138 }
139}
140
141impl ToStringOption for Resp3Frame {
142 fn to_string_option(&self) -> Option<String> {
143 self.to_string()
144 }
145}
146
147pub trait IsNullFrame {
149 fn is_null_frame(&self) -> bool;
150}
151
152impl IsNullFrame for Resp2Frame {
153 fn is_null_frame(&self) -> bool {
154 self == &Resp2Frame::Null
155 }
156}
157
158impl IsNullFrame for Resp3Frame {
159 fn is_null_frame(&self) -> bool {
160 self == &Resp3Frame::Null
161 }
162}
163
164pub trait ToInteger {
166 fn to_integer(&self) -> Option<i64>;
168}
169
170impl ToInteger for Resp2Frame {
171 fn to_integer(&self) -> Option<i64> {
172 match self {
173 Resp2Frame::Integer(number) => Some(*number),
174 _ => None,
175 }
176 }
177}
178
179impl ToInteger for Resp3Frame {
180 fn to_integer(&self) -> Option<i64> {
181 match self {
182 Resp3Frame::Number { data, attributes: _ } => Some(*data),
183 _ => None,
184 }
185 }
186}
187
188pub trait ToStringBytes {
190 fn to_string_bytes(&self) -> Option<Bytes>;
193}
194
195impl ToStringBytes for Resp2Frame {
196 fn to_string_bytes(&self) -> Option<Bytes> {
197 match self {
198 Resp2Frame::BulkString(data) => Some(data.clone()),
199 _ => None,
200 }
201 }
202}
203
204impl ToStringBytes for Resp3Frame {
205 fn to_string_bytes(&self) -> Option<Bytes> {
206 match self {
207 Resp3Frame::BlobString { data, attributes: _ } => Some(data.clone()),
208 _ => None,
209 }
210 }
211}
212
213pub trait ToBytesVec {
215 fn to_vec(&self) -> Option<Vec<Bytes>>;
218}
219
220impl ToBytesVec for Resp2Frame {
221 fn to_vec(&self) -> Option<Vec<Bytes>> {
222 match self {
223 Resp2Frame::Array(data) => data.iter().map(ToStringBytes::to_string_bytes).collect(),
224 _ => None,
225 }
226 }
227}
228
229impl ToBytesVec for Resp3Frame {
230 fn to_vec(&self) -> Option<Vec<Bytes>> {
231 match self {
232 Resp3Frame::Array { data, attributes: _ } => {
233 data.iter().map(ToStringBytes::to_string_bytes).collect()
234 }
235 _ => None,
236 }
237 }
238}
239
240pub trait ToBytesMap {
242 fn to_map(&self) -> Option<BTreeMap<Bytes, Bytes>>;
245}
246
247impl ToBytesMap for Resp2Frame {
248 fn to_map(&self) -> Option<BTreeMap<Bytes, Bytes>> {
249 let mut map = BTreeMap::new();
250
251 match self {
252 Resp2Frame::Array(array) => {
253 for item in array.chunks(2) {
254 if item.len() < 2 {
255 return None;
256 }
257
258 let field = match &item[0] {
259 Resp2Frame::SimpleString(value) | Resp2Frame::BulkString(value) => value.clone(),
260 _ => return None,
261 };
262
263 let value = match &item[1] {
264 Resp2Frame::SimpleString(value) | Resp2Frame::BulkString(value) => value.clone(),
265 _ => return None,
266 };
267
268 map.insert(field, value);
269 }
270 }
271 _ => return None,
272 }
273
274 Some(map)
275 }
276}
277
278impl ToBytesMap for Resp3Frame {
279 fn to_map(&self) -> Option<BTreeMap<Bytes, Bytes>> {
280 let mut map = BTreeMap::new();
281
282 match self {
283 Resp3Frame::Map { data, attributes: _ } => {
284 for item in data {
285 let field = match item.0 {
286 Resp3Frame::BlobString { data, attributes: _ }
287 | Resp3Frame::SimpleString { data, attributes: _ } => data.clone(),
288 _ => return None,
289 };
290
291 let value = match item.1 {
292 Resp3Frame::BlobString { data, attributes: _ }
293 | Resp3Frame::SimpleString { data, attributes: _ } => data.clone(),
294 _ => return None,
295 };
296
297 map.insert(field, value);
298 }
299 }
300 _ => return None,
301 }
302
303 Some(map)
304 }
305}