1#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct MessageId(heapless::String<36>);
13
14impl MessageId {
15 pub fn as_str(&self) -> &str {
16 &self.0
17 }
18}
19
20impl TryFrom<&str> for MessageId {
21 type Error = ();
22
23 fn try_from(value: &str) -> Result<Self, Self::Error> {
24 heapless::String::try_from(value).map(Self)
25 }
26}
27
28#[cfg(feature = "serde")]
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
37pub struct EmptyPayload;
38
39#[cfg(feature = "serde")]
40impl serde::Serialize for EmptyPayload {
41 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
42 use serde::ser::SerializeMap;
43 serializer.serialize_map(Some(0))?.end()
44 }
45}
46
47#[cfg(feature = "serde")]
48impl<'de> serde::Deserialize<'de> for EmptyPayload {
49 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
50 struct EmptyPayloadVisitor;
51
52 impl<'de> serde::de::Visitor<'de> for EmptyPayloadVisitor {
53 type Value = EmptyPayload;
54
55 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56 f.write_str("a JSON object")
57 }
58
59 fn visit_map<A: serde::de::MapAccess<'de>>(
60 self,
61 mut map: A,
62 ) -> Result<Self::Value, A::Error> {
63 while map
64 .next_entry::<serde::de::IgnoredAny, serde::de::IgnoredAny>()?
65 .is_some()
66 {}
67 Ok(EmptyPayload)
68 }
69 }
70
71 deserializer.deserialize_map(EmptyPayloadVisitor)
72 }
73}
74
75#[cfg(feature = "serde")]
80#[derive(Debug, Clone, PartialEq, Eq)]
81pub struct Call<T> {
82 pub message_id: MessageId,
83 pub payload: T,
84}
85
86#[cfg(feature = "serde")]
87impl<T: crate::Action + serde::Serialize> serde::Serialize for Call<T> {
88 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
89 use serde::ser::SerializeTuple;
90
91 let mut tup = serializer.serialize_tuple(4)?;
92 tup.serialize_element(&2u8)?;
93 tup.serialize_element(&self.message_id)?;
94 tup.serialize_element(T::ACTION)?;
95 tup.serialize_element(&self.payload)?;
96 tup.end()
97 }
98}
99
100#[cfg(feature = "serde")]
101impl<'de, T: crate::Action + serde::Deserialize<'de>> serde::Deserialize<'de> for Call<T> {
102 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
103 struct CallVisitor<T>(core::marker::PhantomData<T>);
104
105 impl<'de, T: crate::Action + serde::Deserialize<'de>> serde::de::Visitor<'de> for CallVisitor<T> {
106 type Value = Call<T>;
107
108 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
109 write!(f, "a CALL array [2, messageId, \"{}\", payload]", T::ACTION)
110 }
111
112 fn visit_seq<A: serde::de::SeqAccess<'de>>(
113 self,
114 mut seq: A,
115 ) -> Result<Self::Value, A::Error> {
116 let message_type_id: u8 = seq
117 .next_element()?
118 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
119 if message_type_id != 2 {
120 return Err(serde::de::Error::invalid_value(
121 serde::de::Unexpected::Unsigned(message_type_id as u64),
122 &"2 (CALL)",
123 ));
124 }
125
126 let message_id: MessageId = seq
127 .next_element()?
128 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
129
130 let action: &str = seq
131 .next_element()?
132 .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
133 if action != T::ACTION {
134 return Err(serde::de::Error::invalid_value(
135 serde::de::Unexpected::Str(action),
136 &T::ACTION,
137 ));
138 }
139
140 let payload: T = seq
141 .next_element()?
142 .ok_or_else(|| serde::de::Error::invalid_length(3, &self))?;
143
144 Ok(Call {
145 message_id,
146 payload,
147 })
148 }
149 }
150
151 deserializer.deserialize_tuple(4, CallVisitor(core::marker::PhantomData))
152 }
153}
154
155#[cfg(feature = "serde")]
160#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct CallResult<T> {
162 pub message_id: MessageId,
163 pub payload: T,
164}
165
166#[cfg(feature = "serde")]
167impl<T: serde::Serialize> serde::Serialize for CallResult<T> {
168 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
169 use serde::ser::SerializeTuple;
170
171 let mut tup = serializer.serialize_tuple(3)?;
172 tup.serialize_element(&3u8)?;
173 tup.serialize_element(&self.message_id)?;
174 tup.serialize_element(&self.payload)?;
175 tup.end()
176 }
177}
178
179#[cfg(feature = "serde")]
180impl<'de, T: serde::Deserialize<'de>> serde::Deserialize<'de> for CallResult<T> {
181 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
182 struct CallResultVisitor<T>(core::marker::PhantomData<T>);
183
184 impl<'de, T: serde::Deserialize<'de>> serde::de::Visitor<'de> for CallResultVisitor<T> {
185 type Value = CallResult<T>;
186
187 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
188 f.write_str("a CALLRESULT array [3, messageId, payload]")
189 }
190
191 fn visit_seq<A: serde::de::SeqAccess<'de>>(
192 self,
193 mut seq: A,
194 ) -> Result<Self::Value, A::Error> {
195 let message_type_id: u8 = seq
196 .next_element()?
197 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
198 if message_type_id != 3 {
199 return Err(serde::de::Error::invalid_value(
200 serde::de::Unexpected::Unsigned(message_type_id as u64),
201 &"3 (CALLRESULT)",
202 ));
203 }
204
205 let message_id: MessageId = seq
206 .next_element()?
207 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
208
209 let payload: T = seq
210 .next_element()?
211 .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
212
213 Ok(CallResult {
214 message_id,
215 payload,
216 })
217 }
218 }
219
220 deserializer.deserialize_tuple(3, CallResultVisitor(core::marker::PhantomData))
221 }
222}
223
224macro_rules! error_envelope {
230 ($name:ident, $discriminant:literal, $expecting:literal) => {
231 #[cfg(feature = "serde")]
232 #[derive(Debug, Clone, PartialEq, Eq)]
233 pub struct $name<E, D = EmptyPayload, const DESC_CAP: usize = 1024> {
234 pub message_id: MessageId,
235 pub error_code: E,
236 pub error_description: heapless::String<DESC_CAP>,
237 pub error_details: D,
238 }
239
240 #[cfg(feature = "serde")]
241 impl<E: serde::Serialize, D: serde::Serialize, const DESC_CAP: usize> serde::Serialize
242 for $name<E, D, DESC_CAP>
243 {
244 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
245 use serde::ser::SerializeTuple;
246
247 let mut tup = serializer.serialize_tuple(5)?;
248 tup.serialize_element(&$discriminant)?;
249 tup.serialize_element(&self.message_id)?;
250 tup.serialize_element(&self.error_code)?;
251 tup.serialize_element(&self.error_description)?;
252 tup.serialize_element(&self.error_details)?;
253 tup.end()
254 }
255 }
256
257 #[cfg(feature = "serde")]
258 impl<'de, E: serde::Deserialize<'de>, D: serde::Deserialize<'de>, const DESC_CAP: usize>
259 serde::Deserialize<'de> for $name<E, D, DESC_CAP>
260 {
261 fn deserialize<Dz: serde::Deserializer<'de>>(deserializer: Dz) -> Result<Self, Dz::Error> {
262 struct Visitor<E, D, const DESC_CAP: usize>(core::marker::PhantomData<(E, D)>);
263
264 impl<'de, E: serde::Deserialize<'de>, D: serde::Deserialize<'de>, const DESC_CAP: usize>
265 serde::de::Visitor<'de> for Visitor<E, D, DESC_CAP>
266 {
267 type Value = $name<E, D, DESC_CAP>;
268
269 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
270 f.write_str($expecting)
271 }
272
273 fn visit_seq<A: serde::de::SeqAccess<'de>>(
274 self,
275 mut seq: A,
276 ) -> Result<Self::Value, A::Error> {
277 let message_type_id: u8 = seq
278 .next_element()?
279 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
280 if message_type_id != $discriminant {
281 return Err(serde::de::Error::invalid_value(
282 serde::de::Unexpected::Unsigned(message_type_id as u64),
283 &$expecting,
284 ));
285 }
286
287 let message_id = seq
288 .next_element()?
289 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
290 let error_code = seq
291 .next_element()?
292 .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
293 let error_description = seq
294 .next_element()?
295 .ok_or_else(|| serde::de::Error::invalid_length(3, &self))?;
296 let error_details = seq
297 .next_element()?
298 .ok_or_else(|| serde::de::Error::invalid_length(4, &self))?;
299
300 Ok($name {
301 message_id,
302 error_code,
303 error_description,
304 error_details,
305 })
306 }
307 }
308
309 deserializer.deserialize_tuple(5, Visitor(core::marker::PhantomData))
310 }
311 }
312 };
313}
314
315error_envelope!(
324 CallError,
325 4u8,
326 "a CALLERROR array [4, messageId, errorCode, errorDescription, errorDetails]"
327);
328
329error_envelope!(
333 CallResultError,
334 5u8,
335 "a CALLRESULTERROR array [5, messageId, errorCode, errorDescription, errorDetails]"
336);
337
338#[cfg(feature = "serde")]
343#[derive(Debug, Clone, PartialEq, Eq)]
344pub struct SendMessage<T> {
345 pub message_id: MessageId,
346 pub payload: T,
347}
348
349#[cfg(feature = "serde")]
350impl<T: crate::Action + serde::Serialize> serde::Serialize for SendMessage<T> {
351 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
352 use serde::ser::SerializeTuple;
353
354 let mut tup = serializer.serialize_tuple(4)?;
355 tup.serialize_element(&6u8)?;
356 tup.serialize_element(&self.message_id)?;
357 tup.serialize_element(T::ACTION)?;
358 tup.serialize_element(&self.payload)?;
359 tup.end()
360 }
361}
362
363#[cfg(feature = "serde")]
364impl<'de, T: crate::Action + serde::Deserialize<'de>> serde::Deserialize<'de> for SendMessage<T> {
365 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
366 struct SendVisitor<T>(core::marker::PhantomData<T>);
367
368 impl<'de, T: crate::Action + serde::Deserialize<'de>> serde::de::Visitor<'de> for SendVisitor<T> {
369 type Value = SendMessage<T>;
370
371 fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
372 write!(f, "a SEND array [6, messageId, \"{}\", payload]", T::ACTION)
373 }
374
375 fn visit_seq<A: serde::de::SeqAccess<'de>>(
376 self,
377 mut seq: A,
378 ) -> Result<Self::Value, A::Error> {
379 let message_type_id: u8 = seq
380 .next_element()?
381 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
382 if message_type_id != 6 {
383 return Err(serde::de::Error::invalid_value(
384 serde::de::Unexpected::Unsigned(message_type_id as u64),
385 &"6 (SEND)",
386 ));
387 }
388
389 let message_id: MessageId = seq
390 .next_element()?
391 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
392
393 let action: &str = seq
394 .next_element()?
395 .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?;
396 if action != T::ACTION {
397 return Err(serde::de::Error::invalid_value(
398 serde::de::Unexpected::Str(action),
399 &T::ACTION,
400 ));
401 }
402
403 let payload: T = seq
404 .next_element()?
405 .ok_or_else(|| serde::de::Error::invalid_length(3, &self))?;
406
407 Ok(SendMessage {
408 message_id,
409 payload,
410 })
411 }
412 }
413
414 deserializer.deserialize_tuple(4, SendVisitor(core::marker::PhantomData))
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 use super::*;
421
422 extern crate std;
426
427 #[cfg(feature = "serde")]
428 #[test]
429 fn empty_payload_serializes_as_an_empty_object() {
430 let mut buf = [0u8; 16];
431 let len = serde_json_core::to_slice(&EmptyPayload, &mut buf).unwrap();
432 assert_eq!(&buf[..len], b"{}");
433 }
434
435 #[cfg(feature = "serde")]
436 #[test]
437 fn empty_payload_accepts_and_discards_a_populated_object() {
438 let (parsed, _): (EmptyPayload, usize) =
439 serde_json_core::from_str(r#"{"foo": 1, "bar": [1, 2, 3]}"#).unwrap();
440 assert_eq!(parsed, EmptyPayload);
441 }
442
443 #[cfg(feature = "serde")]
444 #[test]
445 fn call_serializes_and_round_trips_a_real_message_type() {
446 use crate::v16::{AuthorizeRequest, IdTag};
447
448 let call = Call {
449 message_id: MessageId::try_from("19223201").unwrap(),
450 payload: AuthorizeRequest {
451 id_tag: IdTag::try_from("ABC123").unwrap(),
452 },
453 };
454
455 let mut buf = [0u8; 128];
456 let len = serde_json_core::to_slice(&call, &mut buf).unwrap();
457 assert_eq!(
458 core::str::from_utf8(&buf[..len]).unwrap(),
459 r#"[2,"19223201","Authorize",{"idTag":"ABC123"}]"#
460 );
461
462 let (parsed, _): (Call<AuthorizeRequest>, usize) =
463 serde_json_core::from_slice(&buf[..len]).unwrap();
464 assert_eq!(parsed, call);
465 }
466
467 #[cfg(feature = "serde")]
468 #[test]
469 fn call_rejects_the_wrong_message_type_id() {
470 use crate::v16::AuthorizeRequest;
471
472 let json = r#"[3,"1","Authorize",{"idTag":"ABC123"}]"#;
473 let result: Result<(Call<AuthorizeRequest>, usize), _> = serde_json_core::from_str(json);
474 assert!(result.is_err());
475 }
476
477 #[cfg(feature = "serde")]
478 #[test]
479 fn call_rejects_a_mismatched_action() {
480 use crate::v16::AuthorizeRequest;
481
482 let json = r#"[2,"1","Heartbeat",{"idTag":"ABC123"}]"#;
483 let result: Result<(Call<AuthorizeRequest>, usize), _> = serde_json_core::from_str(json);
484 assert!(result.is_err());
485 }
486
487 #[cfg(all(feature = "serde", not(feature = "alloc")))]
491 #[test]
492 fn call_result_serializes_and_round_trips_a_real_message_type() {
493 use crate::v16::HeartbeatResponse;
494
495 let result: CallResult<HeartbeatResponse> = CallResult {
496 message_id: MessageId::try_from("19223201").unwrap(),
497 payload: HeartbeatResponse {
498 current_time: heapless::String::try_from("2013-02-01T20:53:32.486Z").unwrap(),
499 },
500 };
501
502 let mut buf = [0u8; 128];
503 let len = serde_json_core::to_slice(&result, &mut buf).unwrap();
504 assert_eq!(
505 core::str::from_utf8(&buf[..len]).unwrap(),
506 r#"[3,"19223201",{"currentTime":"2013-02-01T20:53:32.486Z"}]"#
507 );
508
509 let (parsed, _): (CallResult<HeartbeatResponse>, usize) =
510 serde_json_core::from_slice(&buf[..len]).unwrap();
511 assert_eq!(parsed, result);
512 }
513
514 #[cfg(feature = "serde")]
515 #[test]
516 fn call_result_rejects_the_wrong_message_type_id() {
517 use crate::v16::HeartbeatResponse;
518
519 let json = r#"[2,"1",{"currentTime":"2013-02-01T20:53:32.486Z"}]"#;
520 let result: Result<(CallResult<HeartbeatResponse>, usize), _> =
521 serde_json_core::from_str(json);
522 assert!(result.is_err());
523 }
524
525 #[cfg(feature = "serde")]
526 #[test]
527 fn call_error_serializes_and_round_trips_a_real_error_code() {
528 use crate::v16::RpcErrorCode;
529
530 let error: CallError<RpcErrorCode> = CallError {
531 message_id: MessageId::try_from("19223201").unwrap(),
532 error_code: RpcErrorCode::NotImplemented,
533 error_description: heapless::String::try_from("unrecognized action").unwrap(),
534 error_details: EmptyPayload,
535 };
536
537 let mut buf = [0u8; 128];
538 let len = serde_json_core::to_slice(&error, &mut buf).unwrap();
539 assert_eq!(
540 core::str::from_utf8(&buf[..len]).unwrap(),
541 r#"[4,"19223201","NotImplemented","unrecognized action",{}]"#
542 );
543
544 let (parsed, _): (CallError<RpcErrorCode>, usize) =
545 serde_json_core::from_slice(&buf[..len]).unwrap();
546 assert_eq!(parsed, error);
547 }
548
549 #[cfg(feature = "serde")]
550 #[test]
551 fn call_error_rejects_the_wrong_message_type_id() {
552 use crate::v16::RpcErrorCode;
553
554 let json = r#"[5,"1","NotImplemented","",{}]"#;
555 let result: Result<(CallError<RpcErrorCode>, usize), _> = serde_json_core::from_str(json);
556 assert!(result.is_err());
557 }
558
559 #[cfg(feature = "serde")]
560 #[test]
561 fn call_result_error_uses_discriminant_five_not_four() {
562 use crate::v201::RpcErrorCode;
563
564 let error: CallResultError<RpcErrorCode> = CallResultError {
565 message_id: MessageId::try_from("1").unwrap(),
566 error_code: RpcErrorCode::FormatViolation,
567 error_description: heapless::String::try_from("").unwrap(),
568 error_details: EmptyPayload,
569 };
570
571 let mut buf = [0u8; 128];
572 let len = serde_json_core::to_slice(&error, &mut buf).unwrap();
573 assert!(core::str::from_utf8(&buf[..len]).unwrap().starts_with("[5,"));
574
575 let call_error_json = r#"[4,"1","FormatViolation","",{}]"#;
578 let result: Result<(CallResultError<RpcErrorCode>, usize), _> =
579 serde_json_core::from_str(call_error_json);
580 assert!(result.is_err());
581 }
582
583 #[cfg(all(feature = "serde", not(feature = "alloc")))]
584 #[test]
585 fn send_message_serializes_and_round_trips_a_real_send_type_message() {
586 std::thread::Builder::new()
600 .stack_size(16 * 1024 * 1024)
601 .spawn(|| {
602 use crate::v21::NotifyPeriodicEventStream;
603
604 let send: SendMessage<NotifyPeriodicEventStream> = SendMessage {
605 message_id: MessageId::try_from("19223201").unwrap(),
606 payload: NotifyPeriodicEventStream {
607 basetime: heapless::String::try_from("2024-08-27T12:30:40Z").unwrap(),
608 custom_data: None,
609 data: heapless::Vec::new(),
610 id: 123,
611 pending: 0,
612 },
613 };
614
615 let mut buf = [0u8; 256];
616 let len = serde_json_core::to_slice(&send, &mut buf).unwrap();
617 let json = core::str::from_utf8(&buf[..len]).unwrap();
618 assert!(json.starts_with(r#"[6,"19223201","NotifyPeriodicEventStream","#));
619
620 let (parsed, _): (SendMessage<NotifyPeriodicEventStream>, usize) =
621 serde_json_core::from_slice(&buf[..len]).unwrap();
622 assert_eq!(parsed, send);
623 })
624 .unwrap()
625 .join()
626 .unwrap();
627 }
628
629 #[test]
630 fn message_id_round_trips_a_short_string() {
631 let id = MessageId::try_from("19223201").unwrap();
632 assert_eq!(id.as_str(), "19223201");
633 }
634
635 #[test]
636 fn message_id_rejects_strings_over_36_bytes() {
637 let too_long = "a".repeat(37);
638 assert!(MessageId::try_from(too_long.as_str()).is_err());
639 }
640
641 #[test]
642 fn message_id_accepts_exactly_36_bytes() {
643 let exactly_36 = "a".repeat(36);
644 assert!(MessageId::try_from(exactly_36.as_str()).is_ok());
645 }
646}