1pub struct CommandBuilder<'a, STAGE> {
39 buffer: &'a mut [u8],
40 index: usize,
41 phantom: core::marker::PhantomData<STAGE>,
42}
43
44impl<'a> CommandBuilder<'a, Uninitialized> {
45 pub fn create_test(
49 buffer: &'a mut [u8],
50 at_prefix: bool,
51 ) -> CommandBuilder<'a, Initialized<Test>> {
52 let mut builder = CommandBuilder::<'a, Initialized<Test>> {
53 buffer,
54 index: 0,
55 phantom: Default::default(),
56 };
57
58 if at_prefix {
59 builder.try_append_data(b"AT");
60 }
61
62 builder
63 }
64
65 pub fn create_query(
69 buffer: &'a mut [u8],
70 at_prefix: bool,
71 ) -> CommandBuilder<'a, Initialized<Query>> {
72 let mut builder = CommandBuilder::<'a, Initialized<Query>> {
73 buffer,
74 index: 0,
75 phantom: Default::default(),
76 };
77
78 if at_prefix {
79 builder.try_append_data(b"AT");
80 }
81
82 builder
83 }
84
85 pub fn create_set(
89 buffer: &'a mut [u8],
90 at_prefix: bool,
91 ) -> CommandBuilder<'a, Initialized<Set>> {
92 let mut builder = CommandBuilder::<'a, Initialized<Set>> {
93 buffer,
94 index: 0,
95 phantom: Default::default(),
96 };
97
98 if at_prefix {
99 builder.try_append_data(b"AT");
100 }
101
102 builder
103 }
104
105 pub fn create_execute(
109 buffer: &'a mut [u8],
110 at_prefix: bool,
111 ) -> CommandBuilder<'a, Initialized<Execute>> {
112 let mut builder = CommandBuilder::<'a, Initialized<Execute>> {
113 buffer,
114 index: 0,
115 phantom: Default::default(),
116 };
117
118 if at_prefix {
119 builder.try_append_data(b"AT");
120 }
121
122 builder
123 }
124}
125impl<ANY> CommandBuilder<'_, ANY> {
126 fn try_append_data(&mut self, data: &[u8]) {
131 let data_length = data.len();
132
133 if let Some(buffer_slice) = self.buffer.get_mut(self.index..(self.index + data_length)) {
139 for (buffer, data) in buffer_slice.iter_mut().zip(data) {
141 *buffer = *data;
143 }
144 }
145
146 self.index += data_length;
148 }
149}
150
151impl<'a, N: Nameable> CommandBuilder<'a, Initialized<N>> {
152 pub fn named<T: AsRef<[u8]>>(mut self, name: T) -> CommandBuilder<'a, N> {
154 self.try_append_data(name.as_ref());
155 self.try_append_data(N::NAME_SUFFIX);
156
157 CommandBuilder::<'a, N> {
158 buffer: self.buffer,
159 index: self.index,
160 phantom: Default::default(),
161 }
162 }
163}
164
165impl CommandBuilder<'_, Set> {
166 pub fn with_int_parameter<INT: Into<i32>>(mut self, value: INT) -> Self {
168 let mut formatting_buffer = [0; crate::formatter::MAX_INT_DIGITS];
169 self.try_append_data(crate::formatter::write_int(
170 &mut formatting_buffer,
171 value.into(),
172 ));
173 self.try_append_data(b",");
174 self
175 }
176
177 pub fn with_string_parameter<T: AsRef<[u8]>>(mut self, value: T) -> Self {
179 self.try_append_data(b"\"");
180 self.try_append_data(value.as_ref());
181 self.try_append_data(b"\"");
182 self.try_append_data(b",");
183 self
184 }
185
186 pub fn with_optional_int_parameter<INT: Into<i32>>(self, value: Option<INT>) -> Self {
188 match value {
189 None => self.with_empty_parameter(),
190 Some(value) => self.with_int_parameter(value),
191 }
192 }
193
194 pub fn with_optional_string_parameter<T: AsRef<[u8]>>(self, value: Option<T>) -> Self {
196 match value {
197 None => self.with_empty_parameter(),
198 Some(value) => self.with_string_parameter(value),
199 }
200 }
201
202 pub fn with_empty_parameter(mut self) -> Self {
204 self.try_append_data(b",");
205 self
206 }
207
208 pub fn with_raw_parameter<T: AsRef<[u8]>>(mut self, value: T) -> Self {
210 self.try_append_data(value.as_ref());
211 self.try_append_data(b",");
212 self
213 }
214}
215
216impl<'a, F: Finishable> CommandBuilder<'a, F> {
217 pub fn finish(self) -> Result<&'a [u8], usize> {
228 self.finish_with(b"\r\n")
229 }
230
231 pub fn finish_with(mut self, terminator: &[u8]) -> Result<&'a [u8], usize> {
257 if let Some(c) = self.buffer.get(self.index - 1) {
259 if *c == b',' {
260 self.index -= 1;
261 }
262 }
263 self.try_append_data(terminator);
264
265 if self.index > self.buffer.len() {
266 Err(self.index)
267 } else {
268 Ok(&self.buffer[0..self.index])
269 }
270 }
271}
272
273pub struct Uninitialized;
275pub struct Initialized<T>(core::marker::PhantomData<T>);
278
279pub struct Test;
281pub struct Query;
283pub struct Set;
285pub struct Execute;
287
288pub trait Finishable {}
290impl Finishable for Test {}
291impl Finishable for Query {}
292impl Finishable for Set {}
293impl Finishable for Execute {}
294
295pub trait Nameable {
297 const NAME_SUFFIX: &'static [u8];
299}
300impl Nameable for Test {
301 const NAME_SUFFIX: &'static [u8] = b"=?";
302}
303impl Nameable for Query {
304 const NAME_SUFFIX: &'static [u8] = b"?";
305}
306impl Nameable for Set {
307 const NAME_SUFFIX: &'static [u8] = b"=";
308}
309impl Nameable for Execute {
310 const NAME_SUFFIX: &'static [u8] = b"";
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn test_command() {
319 let mut buffer = [0; 128];
320 let value = CommandBuilder::create_test(&mut buffer, true)
321 .named("+TEST")
322 .finish()
323 .unwrap();
324
325 assert_eq!(core::str::from_utf8(value).unwrap(), "AT+TEST=?\r\n");
326 }
327
328 #[test]
329 fn test_query() {
330 let mut buffer = [0; 128];
331 let value = CommandBuilder::create_query(&mut buffer, true)
332 .named("+QUERY")
333 .finish()
334 .unwrap();
335
336 assert_eq!(core::str::from_utf8(value).unwrap(), "AT+QUERY?\r\n");
337 }
338
339 #[test]
340 fn test_set() {
341 let mut buffer = [0; 128];
342 let value = CommandBuilder::create_set(&mut buffer, true)
343 .named("+SET")
344 .with_int_parameter(12345)
345 .with_string_parameter("my_string_param")
346 .with_int_parameter(67)
347 .with_int_parameter(89)
348 .finish()
349 .unwrap();
350
351 assert_eq!(
352 core::str::from_utf8(value).unwrap(),
353 "AT+SET=12345,\"my_string_param\",67,89\r\n"
354 );
355 }
356
357 #[test]
358 fn test_execute() {
359 let mut buffer = [0; 128];
360 let value = CommandBuilder::create_execute(&mut buffer, true)
361 .named("+EXECUTE")
362 .finish()
363 .unwrap();
364
365 assert_eq!(core::str::from_utf8(value).unwrap(), "AT+EXECUTE\r\n");
366 }
367
368 #[test]
369 fn test_buffer_too_short() {
370 let mut buffer = [0; 5];
371 assert!(CommandBuilder::create_execute(&mut buffer, true)
372 .named("+BUFFERLENGTH")
373 .finish()
374 .is_err());
375 assert!(CommandBuilder::create_execute(&mut buffer, true)
376 .named("+A")
377 .finish()
378 .is_err()); }
380
381 #[test]
382 fn test_buffer_exact_size() {
383 let mut buffer = [0; 32];
384 let value = CommandBuilder::create_execute(&mut buffer[..8], true)
385 .named("+GMR")
386 .finish()
387 .unwrap();
388
389 assert_eq!(core::str::from_utf8(value).unwrap(), "AT+GMR\r\n");
390
391 let value = CommandBuilder::create_set(&mut buffer[..19], true)
392 .named("+CWRECONNCFG")
393 .with_int_parameter(15)
394 .finish()
395 .unwrap();
396
397 assert_eq!(
398 core::str::from_utf8(value).unwrap(),
399 "AT+CWRECONNCFG=15\r\n"
400 );
401
402 let value = CommandBuilder::create_query(&mut buffer[..14], true)
403 .named("+UART_CUR")
404 .finish()
405 .unwrap();
406
407 assert_eq!(core::str::from_utf8(value).unwrap(), "AT+UART_CUR?\r\n");
408 }
409
410 #[test]
411 fn test_terminator() {
412 let mut buffer = [0; 128];
413 let value = CommandBuilder::create_test(&mut buffer, true)
414 .named("+TEST")
415 .finish_with(b"\0")
416 .unwrap();
417
418 assert_eq!(core::str::from_utf8(value).unwrap(), "AT+TEST=?\0");
419 }
420
421 #[test]
422 fn test_optional() {
423 let mut buffer = [0; 128];
424 let value = CommandBuilder::create_set(&mut buffer, true)
425 .named("+CCUG")
426 .with_empty_parameter()
427 .with_optional_int_parameter(Some(9))
428 .finish_with(b"\r")
429 .unwrap();
430 assert_eq!(core::str::from_utf8(value).unwrap(), "AT+CCUG=,9\r");
433
434 let value = CommandBuilder::create_set(&mut buffer, true)
435 .named("+BLEGATTSSETATTR")
436 .with_int_parameter(1)
437 .with_int_parameter(1)
438 .with_empty_parameter()
439 .with_int_parameter(4)
440 .finish()
441 .unwrap();
442 assert_eq!(
444 core::str::from_utf8(value).unwrap(),
445 "AT+BLEGATTSSETATTR=1,1,,4\r\n"
446 );
447
448 let value = CommandBuilder::create_set(&mut buffer, true)
449 .named("+HTTPCLIENT")
450 .with_int_parameter(2)
451 .with_int_parameter(1)
452 .with_optional_string_parameter(Some("http://localpc/ip"))
453 .with_empty_parameter()
454 .with_empty_parameter()
455 .with_int_parameter(1)
456 .finish()
457 .unwrap();
458
459 assert_eq!(
460 core::str::from_utf8(value).unwrap(),
461 "AT+HTTPCLIENT=2,1,\"http://localpc/ip\",,,1\r\n"
462 );
463 }
464
465 #[test]
466 fn test_raw_parameter() {
467 let mut buffer = [0; 128];
468 let value = CommandBuilder::create_set(&mut buffer, true)
469 .named("+CPIN")
470 .with_raw_parameter(b"1234")
471 .with_optional_int_parameter(Some(9))
472 .finish_with(b"\r")
473 .unwrap();
474 assert_eq!(core::str::from_utf8(value).unwrap(), "AT+CPIN=1234,9\r");
475 }
476}