macro_rules! u8_enum {
(
$(#[$meta:meta])*
$vis:vis enum $name:ident, $what:literal {
$( $variant:ident = $val:literal ),+ $(,)?
}
) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
$vis enum $name {
$( $variant = $val, )+
}
impl $name {
fn from_u8(v: u8) -> crate::error::MqttResult<Self> {
Ok(match v {
$( $val => $name::$variant, )+
other => {
return Err(crate::error::MqttError::MalformedPacket(format!(
concat!("unknown ", $what, " {}"),
other
)))
}
})
}
}
};
}
pub(crate) use u8_enum;
macro_rules! mqtt_properties {
( $( $id:literal => $variant:ident : $kind:ident ),+ $(,)? ) => {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Property {
$( $variant( mqtt_properties!(@ty $kind) ), )+
}
impl Property {
fn identifier(&self) -> u32 {
match self {
$( Property::$variant(..) => $id, )+
}
}
fn encode(&self, out: &mut BytesMut) -> MqttResult<()> {
encode_varint(self.identifier(), out)?;
mqtt_properties!(@encode self, out, []; $( $variant : $kind )+)
}
fn encoded_len(&self) -> usize {
let id_len = varint_len(self.identifier());
let body_len = mqtt_properties!(@len self, []; $( $variant : $kind )+);
id_len + body_len
}
fn decode(id: u32, buf: &mut Bytes) -> MqttResult<Self> {
mqtt_properties!(@decode id, buf, []; $( $id => $variant : $kind )+)
}
}
};
(@ty u8) => { u8 };
(@ty u16) => { u16 };
(@ty u32) => { u32 };
(@ty varint32) => { u32 };
(@ty str) => { String };
(@ty bin) => { Bytes };
(@ty strpair) => { (String, String) };
(@encode $self:ident, $out:ident, [$($acc:tt)*]; $variant:ident : u8 $($rest:tt)*) => {
mqtt_properties!(@encode $self, $out, [$($acc)*
Property::$variant(v) => { $out.put_u8(*v); Ok(()) },
]; $($rest)*)
};
(@encode $self:ident, $out:ident, [$($acc:tt)*]; $variant:ident : u16 $($rest:tt)*) => {
mqtt_properties!(@encode $self, $out, [$($acc)*
Property::$variant(v) => { $out.put_u16(*v); Ok(()) },
]; $($rest)*)
};
(@encode $self:ident, $out:ident, [$($acc:tt)*]; $variant:ident : u32 $($rest:tt)*) => {
mqtt_properties!(@encode $self, $out, [$($acc)*
Property::$variant(v) => { $out.put_u32(*v); Ok(()) },
]; $($rest)*)
};
(@encode $self:ident, $out:ident, [$($acc:tt)*]; $variant:ident : varint32 $($rest:tt)*) => {
mqtt_properties!(@encode $self, $out, [$($acc)*
Property::$variant(v) => encode_varint(*v, $out),
]; $($rest)*)
};
(@encode $self:ident, $out:ident, [$($acc:tt)*]; $variant:ident : str $($rest:tt)*) => {
mqtt_properties!(@encode $self, $out, [$($acc)*
Property::$variant(v) => encode_utf8_string(v, $out),
]; $($rest)*)
};
(@encode $self:ident, $out:ident, [$($acc:tt)*]; $variant:ident : bin $($rest:tt)*) => {
mqtt_properties!(@encode $self, $out, [$($acc)*
Property::$variant(v) => encode_binary(v, $out),
]; $($rest)*)
};
(@encode $self:ident, $out:ident, [$($acc:tt)*]; $variant:ident : strpair $($rest:tt)*) => {
mqtt_properties!(@encode $self, $out, [$($acc)*
Property::$variant((k, v)) => { encode_utf8_string(k, $out)?; encode_utf8_string(v, $out) },
]; $($rest)*)
};
(@encode $self:ident, $out:ident, [$($acc:tt)*];) => {
match $self { $($acc)* }
};
(@len $self:ident, [$($acc:tt)*]; $variant:ident : u8 $($rest:tt)*) => {
mqtt_properties!(@len $self, [$($acc)* Property::$variant(_) => 1,]; $($rest)*)
};
(@len $self:ident, [$($acc:tt)*]; $variant:ident : u16 $($rest:tt)*) => {
mqtt_properties!(@len $self, [$($acc)* Property::$variant(_) => 2,]; $($rest)*)
};
(@len $self:ident, [$($acc:tt)*]; $variant:ident : u32 $($rest:tt)*) => {
mqtt_properties!(@len $self, [$($acc)* Property::$variant(_) => 4,]; $($rest)*)
};
(@len $self:ident, [$($acc:tt)*]; $variant:ident : varint32 $($rest:tt)*) => {
mqtt_properties!(@len $self, [$($acc)* Property::$variant(v) => varint_len(*v),]; $($rest)*)
};
(@len $self:ident, [$($acc:tt)*]; $variant:ident : str $($rest:tt)*) => {
mqtt_properties!(@len $self, [$($acc)* Property::$variant(v) => 2 + v.len(),]; $($rest)*)
};
(@len $self:ident, [$($acc:tt)*]; $variant:ident : bin $($rest:tt)*) => {
mqtt_properties!(@len $self, [$($acc)* Property::$variant(v) => 2 + v.len(),]; $($rest)*)
};
(@len $self:ident, [$($acc:tt)*]; $variant:ident : strpair $($rest:tt)*) => {
mqtt_properties!(@len $self, [$($acc)* Property::$variant((k, v)) => 2 + k.len() + 2 + v.len(),]; $($rest)*)
};
(@len $self:ident, [$($acc:tt)*];) => {
match $self { $($acc)* }
};
(@decode $id:ident, $buf:ident, [$($acc:tt)*]; $lit:literal => $variant:ident : u8 $($rest:tt)*) => {
mqtt_properties!(@decode $id, $buf, [$($acc)* $lit => read_u8($buf).map(Property::$variant),]; $($rest)*)
};
(@decode $id:ident, $buf:ident, [$($acc:tt)*]; $lit:literal => $variant:ident : u16 $($rest:tt)*) => {
mqtt_properties!(@decode $id, $buf, [$($acc)* $lit => read_u16($buf).map(Property::$variant),]; $($rest)*)
};
(@decode $id:ident, $buf:ident, [$($acc:tt)*]; $lit:literal => $variant:ident : u32 $($rest:tt)*) => {
mqtt_properties!(@decode $id, $buf, [$($acc)* $lit => read_u32($buf).map(Property::$variant),]; $($rest)*)
};
(@decode $id:ident, $buf:ident, [$($acc:tt)*]; $lit:literal => $variant:ident : varint32 $($rest:tt)*) => {
mqtt_properties!(@decode $id, $buf, [$($acc)*
$lit => decode_varint($buf).and_then(|o| o.ok_or_else(|| truncated("varint property"))).map(Property::$variant),
]; $($rest)*)
};
(@decode $id:ident, $buf:ident, [$($acc:tt)*]; $lit:literal => $variant:ident : str $($rest:tt)*) => {
mqtt_properties!(@decode $id, $buf, [$($acc)* $lit => decode_utf8_string($buf).map(Property::$variant),]; $($rest)*)
};
(@decode $id:ident, $buf:ident, [$($acc:tt)*]; $lit:literal => $variant:ident : bin $($rest:tt)*) => {
mqtt_properties!(@decode $id, $buf, [$($acc)* $lit => decode_binary($buf).map(Property::$variant),]; $($rest)*)
};
(@decode $id:ident, $buf:ident, [$($acc:tt)*]; $lit:literal => $variant:ident : strpair $($rest:tt)*) => {
mqtt_properties!(@decode $id, $buf, [$($acc)*
$lit => decode_utf8_string($buf).and_then(|k| Ok(Property::$variant((k, decode_utf8_string($buf)?)))),
]; $($rest)*)
};
(@decode $id:ident, $buf:ident, [$($acc:tt)*];) => {
match $id {
$($acc)*
other => Err(MqttError::MalformedPacket(format!("unknown property identifier {other}"))),
}
};
}
pub(crate) use mqtt_properties;