Skip to main content

mountain_mqtt/data/
property.rs

1use crate::data::string_pair::StringPair;
2
3use crate::codec::{
4    mqtt_reader::{self, MqttReader},
5    mqtt_writer::{self, MqttWriter},
6    read::Read,
7    write::Write,
8};
9
10use core::marker::PhantomData;
11pub trait Property<'a, T> {
12    const IDENTIFIER: u32;
13    fn value(&self) -> T;
14}
15
16#[macro_export]
17macro_rules! property_owned {
18    ( $n:ident, $t:ty, $c:literal ) => {
19        #[derive(Debug, PartialEq)]
20        pub struct $n<'a> {
21            value: $t,
22            phantom: PhantomData<&'a $t>,
23        }
24
25        impl<'a> $n<'a> {
26            pub fn new(value: $t) -> Self {
27                Self {
28                    value,
29                    phantom: PhantomData,
30                }
31            }
32        }
33
34        impl Write for $n<'_> {
35            fn write<'a, W: MqttWriter<'a> + ?Sized>(
36                &self,
37                writer: &mut W,
38            ) -> mqtt_writer::Result<()> {
39                self.value.write(writer)
40            }
41        }
42
43        impl<'a> Read<'a> for $n<'_> {
44            fn read<R: MqttReader<'a>>(reader: &mut R) -> mqtt_reader::Result<Self>
45            where
46                Self: Sized,
47            {
48                let value = <$t as Read>::read(reader)?;
49                Ok(Self::new(value))
50            }
51        }
52
53        impl<'a> Property<'a, $t> for $n<'a> {
54            const IDENTIFIER: u32 = $c;
55            fn value(&self) -> $t {
56                self.value
57            }
58        }
59
60        impl From<$t> for $n<'_> {
61            fn from(value: $t) -> Self {
62                Self::new(value)
63            }
64        }
65    };
66}
67
68#[macro_export]
69macro_rules! property_variable_u32 {
70    ( $n:ident, $c:literal ) => {
71        #[derive(Debug, PartialEq)]
72        pub struct $n<'a> {
73            value: u32,
74            phantom: PhantomData<&'a u32>,
75        }
76
77        impl<'a> $n<'a> {
78            pub fn new(value: u32) -> Self {
79                Self {
80                    value,
81                    phantom: PhantomData,
82                }
83            }
84        }
85
86        impl Write for $n<'_> {
87            fn write<'a, W: MqttWriter<'a> + ?Sized>(
88                &self,
89                writer: &mut W,
90            ) -> mqtt_writer::Result<()> {
91                writer.put_variable_u32(self.value)
92            }
93        }
94
95        impl<'a> Read<'a> for $n<'_> {
96            fn read<R: MqttReader<'a>>(reader: &mut R) -> mqtt_reader::Result<Self>
97            where
98                Self: Sized,
99            {
100                let value = reader.get_variable_u32()?;
101                Ok(Self::new(value))
102            }
103        }
104
105        impl<'a> Property<'a, u32> for $n<'a> {
106            const IDENTIFIER: u32 = $c;
107            fn value(&self) -> u32 {
108                self.value
109            }
110        }
111
112        impl From<u32> for $n<'_> {
113            fn from(value: u32) -> Self {
114                Self::new(value)
115            }
116        }
117    };
118}
119
120#[macro_export]
121macro_rules! property_str {
122    ( $n:ident, $c:literal ) => {
123        #[derive(Debug, PartialEq)]
124        pub struct $n<'a> {
125            value: &'a str,
126        }
127
128        impl<'a> $n<'a> {
129            pub fn new(value: &'a str) -> Self {
130                Self { value }
131            }
132        }
133
134        impl Write for $n<'_> {
135            fn write<'a, W: MqttWriter<'a>>(&self, writer: &mut W) -> mqtt_writer::Result<()> {
136                writer.put_str(self.value)
137            }
138        }
139
140        impl<'a> Read<'a> for $n<'a> {
141            fn read<R: MqttReader<'a>>(reader: &mut R) -> mqtt_reader::Result<Self>
142            where
143                Self: Sized,
144            {
145                let value = reader.get_str()?;
146                Ok(Self::new(value))
147            }
148        }
149
150        impl<'a> Property<'a, &'a str> for $n<'a> {
151            const IDENTIFIER: u32 = $c;
152            fn value(&self) -> &'a str {
153                self.value
154            }
155        }
156
157        impl<'a> From<&'a str> for $n<'a> {
158            fn from(value: &'a str) -> Self {
159                Self::new(value)
160            }
161        }
162    };
163}
164
165#[macro_export]
166macro_rules! property_string_pair {
167    ( $n:ident, $c:literal ) => {
168        #[derive(Debug, PartialEq)]
169        pub struct $n<'a> {
170            value: StringPair<'a>,
171        }
172
173        impl<'a> $n<'a> {
174            pub fn new(value: StringPair<'a>) -> Self {
175                Self { value }
176            }
177        }
178
179        impl Write for $n<'_> {
180            fn write<'a, W: MqttWriter<'a>>(&self, writer: &mut W) -> mqtt_writer::Result<()> {
181                writer.put_string_pair(&self.value)
182            }
183        }
184
185        impl<'a> Read<'a> for $n<'a> {
186            fn read<R: MqttReader<'a>>(reader: &mut R) -> mqtt_reader::Result<Self>
187            where
188                Self: Sized,
189            {
190                let value = reader.get_string_pair()?;
191                Ok(Self::new(value))
192            }
193        }
194
195        impl<'a> Property<'a, StringPair<'a>> for $n<'a> {
196            const IDENTIFIER: u32 = $c;
197            fn value(&self) -> StringPair<'a> {
198                self.value
199            }
200        }
201
202        impl<'a> From<StringPair<'a>> for $n<'a> {
203            fn from(value: StringPair<'a>) -> Self {
204                Self::new(value)
205            }
206        }
207    };
208}
209
210#[macro_export]
211macro_rules! property_binary_data {
212    ( $n:ident, $c:literal ) => {
213        #[derive(Debug, PartialEq)]
214        pub struct $n<'a> {
215            value: &'a [u8],
216        }
217
218        impl<'a> $n<'a> {
219            pub fn new(value: &'a [u8]) -> Self {
220                Self { value }
221            }
222        }
223
224        impl Write for $n<'_> {
225            fn write<'a, W: MqttWriter<'a>>(&self, writer: &mut W) -> mqtt_writer::Result<()> {
226                writer.put_binary_data(self.value)
227            }
228        }
229
230        impl<'a> Read<'a> for $n<'a> {
231            fn read<R: MqttReader<'a>>(reader: &mut R) -> mqtt_reader::Result<Self>
232            where
233                Self: Sized,
234            {
235                let value = reader.get_binary_data()?;
236                Ok(Self::new(value))
237            }
238        }
239
240        impl<'a> Property<'a, &'a [u8]> for $n<'a> {
241            const IDENTIFIER: u32 = $c;
242            fn value(&self) -> &'a [u8] {
243                self.value
244            }
245        }
246
247        impl<'a> From<&'a [u8]> for $n<'a> {
248            fn from(value: &'a [u8]) -> Self {
249                Self::new(value)
250            }
251        }
252    };
253}
254
255#[macro_export]
256macro_rules! packet_properties {
257    ( $n:ident, [ $( $p:ident ),+ ] ) => {
258
259        #[derive(Debug, PartialEq)]
260        pub enum $n<'a>{
261            $(
262                $p($p<'a>),
263            )*
264        }
265
266        impl Write for $n<'_> {
267            fn write<'a, W: MqttWriter<'a>>(&self, writer: &mut W) -> mqtt_writer::Result<()> {
268                match self {
269                    $(
270                        Self::$p(v) => {
271                            writer.put_variable_u32($p::IDENTIFIER)?;
272                            v.write(writer)?;
273                            Ok(())
274                        }
275                    )*
276                }
277            }
278        }
279
280        impl<'a> Read<'a> for $n<'a> {
281            fn read<R: MqttReader<'a>>(reader: &mut R) -> mqtt_reader::Result<Self>
282            where
283                Self: Sized,
284            {
285                let id = reader.get_variable_u32()?;
286                match id {
287                    $(
288                        $p::IDENTIFIER => {
289                            let v = $p::read(reader)?;
290                            Ok(Self::$p(v))
291                        }
292                    )*
293                    _ => Err($crate::error::PacketReadError::UnexpectedPropertyIdentifier),
294                }
295            }
296        }
297    };
298}
299
300// Macros define the struct for each property type, and implement Read and Write for it
301property_owned!(PayloadFormatIndicator, u8, 0x01);
302property_owned!(MessageExpiryInterval, u32, 0x02);
303property_str!(ContentType, 0x03);
304property_str!(ResponseTopic, 0x08);
305property_binary_data!(CorrelationData, 0x09);
306property_variable_u32!(SubscriptionIdentifier, 0x0B);
307property_owned!(SessionExpiryInterval, u32, 0x11);
308property_str!(AssignedClientIdentifier, 0x12);
309property_owned!(ServerKeepAlive, u16, 0x13);
310property_str!(AuthenticationMethod, 0x15);
311property_binary_data!(AuthenticationData, 0x16);
312property_owned!(RequestProblemInformation, u8, 0x17);
313property_owned!(WillDelayInterval, u32, 0x18);
314property_owned!(RequestResponseInformation, u8, 0x19);
315property_str!(ResponseInformation, 0x1A);
316property_str!(ServerReference, 0x1C);
317property_str!(ReasonString, 0x1F);
318property_owned!(ReceiveMaximum, u16, 0x21);
319property_owned!(TopicAliasMaximum, u16, 0x22);
320property_owned!(TopicAlias, u16, 0x23);
321property_owned!(MaximumQos, u8, 0x24);
322property_owned!(RetainAvailable, u8, 0x25);
323property_string_pair!(UserProperty, 0x26);
324property_owned!(MaximumPacketSize, u32, 0x27);
325property_owned!(WildcardSubscriptionAvailable, u8, 0x28);
326property_owned!(SubscriptionIdentifierAvailable, u8, 0x29);
327property_owned!(SharedSubscriptionAvailable, u8, 0x2A);
328
329packet_properties!(
330    ConnectProperty,
331    [
332        SessionExpiryInterval,
333        AuthenticationMethod,
334        AuthenticationData,
335        RequestProblemInformation,
336        RequestResponseInformation,
337        ReceiveMaximum,
338        TopicAliasMaximum,
339        UserProperty,
340        MaximumPacketSize
341    ]
342);
343packet_properties!(
344    ConnackProperty,
345    [
346        SessionExpiryInterval,
347        AssignedClientIdentifier,
348        ServerKeepAlive,
349        AuthenticationMethod,
350        AuthenticationData,
351        ResponseInformation,
352        ServerReference,
353        ReasonString,
354        ReceiveMaximum,
355        TopicAliasMaximum,
356        MaximumQos,
357        RetainAvailable,
358        UserProperty,
359        MaximumPacketSize,
360        WildcardSubscriptionAvailable,
361        SubscriptionIdentifierAvailable,
362        SharedSubscriptionAvailable
363    ]
364);
365
366packet_properties!(
367    PublishProperty,
368    [
369        PayloadFormatIndicator,
370        MessageExpiryInterval,
371        ContentType,
372        ResponseTopic,
373        CorrelationData,
374        SubscriptionIdentifier,
375        TopicAlias,
376        UserProperty
377    ]
378);
379packet_properties!(PubackProperty, [ReasonString, UserProperty]);
380packet_properties!(PubrecProperty, [ReasonString, UserProperty]);
381packet_properties!(PubrelProperty, [ReasonString, UserProperty]);
382packet_properties!(PubcompProperty, [ReasonString, UserProperty]);
383
384packet_properties!(SubscribeProperty, [SubscriptionIdentifier, UserProperty]);
385packet_properties!(SubackProperty, [ReasonString, UserProperty]);
386
387packet_properties!(UnsubscribeProperty, [UserProperty]);
388packet_properties!(UnsubackProperty, [ReasonString, UserProperty]);
389
390packet_properties!(
391    DisconnectProperty,
392    [
393        SessionExpiryInterval,
394        ServerReference,
395        ReasonString,
396        UserProperty
397    ]
398);
399
400packet_properties!(
401    AuthProperty,
402    [
403        AuthenticationMethod,
404        AuthenticationData,
405        ReasonString,
406        UserProperty
407    ]
408);
409
410packet_properties!(
411    WillProperty,
412    [
413        PayloadFormatIndicator,
414        MessageExpiryInterval,
415        ContentType,
416        ResponseTopic,
417        CorrelationData,
418        WillDelayInterval,
419        UserProperty
420    ]
421);
422
423#[cfg(test)]
424mod tests {
425    use heapless::Vec;
426
427    use crate::{
428        codec::{mqtt_reader::MqttBufReader, mqtt_writer::MqttBufWriter},
429        error::PacketReadError,
430    };
431
432    use super::*;
433
434    property_owned!(PropertyU8, u8, 0x01);
435    property_owned!(PropertyU16, u16, 0x02);
436    property_owned!(PropertyU32, u32, 0x03);
437    property_variable_u32!(PropertyVariableU32, 0x04);
438    property_str!(PropertyString, 0x05);
439    property_string_pair!(PropertyStringPair, 0x06);
440    property_binary_data!(PropertyBinaryData, 0x07);
441
442    packet_properties!(
443        PacketAnyProperty,
444        [
445            PropertyU8,
446            PropertyU16,
447            PropertyU32,
448            PropertyVariableU32,
449            PropertyString,
450            PropertyStringPair,
451            PropertyBinaryData
452        ]
453    );
454
455    packet_properties!(
456        PacketFirstThreeProperty,
457        [PropertyU8, PropertyU16, PropertyU32]
458    );
459
460    #[test]
461    fn write_and_read_a_property_u8() {
462        let mut buf = [0xFFu8; 4];
463        let p = PropertyU8::new(42);
464        let position = {
465            let mut r = MqttBufWriter::new(&mut buf);
466
467            p.write(&mut r).unwrap();
468            r.position()
469        };
470        assert_eq!(buf[0..position], [42u8]);
471
472        let mut r = MqttBufReader::new(&buf);
473        let read_p = PropertyU8::read(&mut r).unwrap();
474        assert_eq!(read_p, p);
475    }
476
477    #[test]
478    fn write_and_read_a_packet_property_u8() {
479        let mut buf = [0xFFu8; 4];
480
481        let p = PacketAnyProperty::PropertyU8(PropertyU8::new(42));
482
483        let position = {
484            let mut r = MqttBufWriter::new(&mut buf);
485
486            p.write(&mut r).unwrap();
487            r.position()
488        };
489        assert_eq!(
490            buf[0..position],
491            // Note the id is a variable byte integer - we know it encodes as one byte so just cast it
492            [PropertyU8::IDENTIFIER as u8, 42u8]
493        );
494
495        let mut r = MqttBufReader::new(&buf);
496        let read_p = PacketAnyProperty::read(&mut r).unwrap();
497        assert_eq!(read_p, p);
498    }
499
500    #[test]
501    fn write_and_read_a_full_set_of_properties() {
502        let mut buf = [0xFFu8; 1024];
503        let data: &[u8] = &[1u8, 2, 3, 4, 5, 6];
504
505        let properties = [
506            PacketAnyProperty::PropertyU8(1.into()),
507            PacketAnyProperty::PropertyU16(2.into()),
508            PacketAnyProperty::PropertyU32(3.into()),
509            PacketAnyProperty::PropertyVariableU32(4.into()),
510            PacketAnyProperty::PropertyString("hello world".into()),
511            PacketAnyProperty::PropertyStringPair(StringPair::new("name", "value").into()),
512            PacketAnyProperty::PropertyBinaryData(data.into()),
513        ];
514
515        let position = {
516            let mut r = MqttBufWriter::new(&mut buf);
517
518            for p in properties.iter() {
519                p.write(&mut r).unwrap();
520            }
521            r.position()
522        };
523
524        let mut r = MqttBufReader::new(&buf[0..position]);
525        for p in properties.iter() {
526            let p_read = PacketAnyProperty::read(&mut r).unwrap();
527            assert_eq!(&p_read, p);
528        }
529    }
530
531    #[test]
532    fn write_and_read_a_variable_u32_delimited_vec_of_properties() {
533        let data: &[u8] = &[1u8, 2, 3, 4, 5, 6];
534
535        let mut vec: Vec<PacketAnyProperty<'_>, 16> = Vec::new();
536        vec.extend([
537            PacketAnyProperty::PropertyU8(1.into()),
538            PacketAnyProperty::PropertyU16(2.into()),
539            PacketAnyProperty::PropertyU32(3.into()),
540            PacketAnyProperty::PropertyVariableU32(4.into()),
541            PacketAnyProperty::PropertyString("hello world".into()),
542            PacketAnyProperty::PropertyStringPair(StringPair::new("name", "value").into()),
543            PacketAnyProperty::PropertyBinaryData(data.into()),
544        ]);
545
546        let mut buf = [0xFFu8; 1024];
547        let encoded_length = {
548            let mut r = MqttBufWriter::new(&mut buf);
549            r.put_variable_u32_delimited_vec(&vec).unwrap();
550            r.position()
551        };
552
553        // Check length is as expected
554        // 7 single-byte property identifiers
555        // 8 bytes total for u8, u16, u32 and single-byte variable u32
556        // 2 + 11 bytes for length and utf8 bytes of "hello world"
557        // 2 + 4 + 2 + 5 bytes for lengths and utf8 bytes of string pair
558        // 2 + 6 for bytes length and contents of data
559        // = 49 byte payload
560        // + 1 for the variable u32 length of the whole encoded properties list
561        // = 50 bytes
562        assert_eq!(encoded_length, 50);
563
564        // Decode again - provide only the exact amount of data, to check
565        // that getting properties respects encoded length
566        let mut r = MqttBufReader::new(&buf[0..encoded_length]);
567        let mut read_vec: Vec<PacketAnyProperty<'_>, 16> = Vec::new();
568        r.get_property_list(&mut read_vec).unwrap();
569
570        assert_eq!(0, r.remaining());
571        assert_eq!(read_vec, vec);
572
573        // Expected error for decoding into too short a vec
574        let mut r = MqttBufReader::new(&buf[0..encoded_length]);
575        let mut read_vec: Vec<PacketAnyProperty<'_>, 3> = Vec::new();
576        assert_eq!(
577            r.get_property_list(&mut read_vec),
578            Err(PacketReadError::TooManyProperties)
579        );
580    }
581
582    #[test]
583    fn write_and_read_expected_subset_of_properties_for_packet() {
584        let mut buf = [0xFFu8; 1024];
585
586        // These properties are from "PacketAny" that can accept any property,
587        // but are also accepted by PacketFirstThreeProperty
588        let properties = [
589            PacketAnyProperty::PropertyU8(1.into()),
590            PacketAnyProperty::PropertyU16(2.into()),
591            PacketAnyProperty::PropertyU32(3.into()),
592        ];
593
594        let expected_properties = [
595            PacketFirstThreeProperty::PropertyU8(1.into()),
596            PacketFirstThreeProperty::PropertyU16(2.into()),
597            PacketFirstThreeProperty::PropertyU32(3.into()),
598        ];
599
600        // Write out the properties as properties of PacketAnyProperty
601        let position = {
602            let mut r = MqttBufWriter::new(&mut buf);
603
604            for p in properties.iter() {
605                p.write(&mut r).unwrap();
606            }
607            r.position()
608        };
609
610        // Read back as PacketFirstThreeProperty properties, so we can check they work
611        let mut r = MqttBufReader::new(&buf[0..position]);
612        for p in expected_properties.iter() {
613            let p_read = PacketFirstThreeProperty::read(&mut r).unwrap();
614            assert_eq!(&p_read, p);
615        }
616    }
617
618    #[test]
619    fn fail_with_unexpected_property_identifier_on_reading_property_outside_subset_for_packet() {
620        // These properties are in PacketAnyProperty but not in PacketFirstThreeProperty,
621        // so reading any of them should fail
622        let data: &[u8] = &[1u8, 2, 3, 4, 5, 6];
623        let unexpected_properties = [
624            PacketAnyProperty::PropertyVariableU32(4.into()),
625            PacketAnyProperty::PropertyString("hello world".into()),
626            PacketAnyProperty::PropertyStringPair(StringPair::new("name", "value").into()),
627            PacketAnyProperty::PropertyBinaryData(data.into()),
628        ];
629
630        let mut buf = [0xFFu8; 1024];
631        for p in unexpected_properties.iter() {
632            buf.fill(0xFFu8);
633            let position = {
634                let mut r = MqttBufWriter::new(&mut buf);
635                p.write(&mut r).unwrap();
636                r.position()
637            };
638
639            let mut r = MqttBufReader::new(&buf[0..position]);
640            assert_eq!(
641                PacketFirstThreeProperty::read(&mut r),
642                Err(PacketReadError::UnexpectedPropertyIdentifier)
643            );
644        }
645    }
646}