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 ToBytesMap {
215 fn to_map(&self) -> Option<BTreeMap<Bytes, Bytes>>;
218}
219
220impl ToBytesMap for Resp2Frame {
221 fn to_map(&self) -> Option<BTreeMap<Bytes, Bytes>> {
222 let mut map = BTreeMap::new();
223
224 match self {
225 Resp2Frame::Array(array) => {
226 for item in array.chunks(2) {
227 if item.len() < 2 {
228 return None;
229 }
230
231 let field = match &item[0] {
232 Resp2Frame::SimpleString(value) | Resp2Frame::BulkString(value) => value.clone(),
233 _ => return None,
234 };
235
236 let value = match &item[1] {
237 Resp2Frame::SimpleString(value) | Resp2Frame::BulkString(value) => value.clone(),
238 _ => return None,
239 };
240
241 map.insert(field, value);
242 }
243 }
244 _ => return None,
245 }
246
247 Some(map)
248 }
249}
250
251impl ToBytesMap for Resp3Frame {
252 fn to_map(&self) -> Option<BTreeMap<Bytes, Bytes>> {
253 let mut map = BTreeMap::new();
254
255 match self {
256 Resp3Frame::Map { data, attributes: _ } => {
257 for item in data {
258 let field = match item.0 {
259 Resp3Frame::BlobString { data, attributes: _ }
260 | Resp3Frame::SimpleString { data, attributes: _ } => data.clone(),
261 _ => return None,
262 };
263
264 let value = match item.1 {
265 Resp3Frame::BlobString { data, attributes: _ }
266 | Resp3Frame::SimpleString { data, attributes: _ } => data.clone(),
267 _ => return None,
268 };
269
270 map.insert(field, value);
271 }
272 }
273 _ => return None,
274 }
275
276 Some(map)
277 }
278}