rust-mqtt 0.1.5

MQTT client for both embedded and non-embedded devices
Documentation
/*
 * MIT License
 *
 * Copyright (c) [2022] [Ondrej Babec <ond.babec@gmail.com>]
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

use crate::encoding::variable_byte_integer::VariableByteIntegerEncoder;
use crate::utils::buffer_reader::BuffReader;
use crate::utils::buffer_writer::BuffWriter;
use crate::utils::types::{BinaryData, BufferError, EncodedString, StringPair};

#[derive(Debug, Clone)]
pub enum Property<'a> {
    PayloadFormat(u8),
    MessageExpiryInterval(u32),
    ContentType(EncodedString<'a>),
    ResponseTopic(EncodedString<'a>),
    CorrelationData(BinaryData<'a>),
    SubscriptionIdentifier(u32),
    SessionExpiryInterval(u32),
    AssignedClientIdentifier(EncodedString<'a>),
    ServerKeepAlive(u16),
    AuthenticationMethod(EncodedString<'a>),
    AuthenticationData(BinaryData<'a>),
    RequestProblemInformation(u8),
    WillDelayInterval(u32),
    RequestResponseInformation(u8),
    ResponseInformation(EncodedString<'a>),
    ServerReference(EncodedString<'a>),
    ReasonString(EncodedString<'a>),
    ReceiveMaximum(u16),
    TopicAliasMaximum(u16),
    TopicAlias(u16),
    MaximumQoS(u8),
    RetainAvailable(u8),
    UserProperty(StringPair<'a>),
    MaximumPacketSize(u32),
    WildcardSubscriptionAvailable(u8),
    SubscriptionIdentifierAvailable(u8),
    SharedSubscriptionAvailable(u8),
    Reserved(),
}

impl<'a> Property<'a> {
    pub fn connect_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::SessionExpiryInterval(_u) => true,
            Property::ReceiveMaximum(_u) => true,
            Property::MaximumPacketSize(_u) => true,
            Property::TopicAliasMaximum(_u) => true,
            Property::RequestResponseInformation(_u) => true,
            Property::RequestProblemInformation(_u) => true,
            Property::UserProperty(_u) => true,
            Property::AuthenticationMethod(_u) => true,
            Property::AuthenticationData(_u) => true,
            _ => false,
        }
    }

    pub fn connack_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::SessionExpiryInterval(_u) => true,
            Property::ReceiveMaximum(_u) => true,
            Property::MaximumQoS(_u) => true,
            Property::MaximumPacketSize(_u) => true,
            Property::AssignedClientIdentifier(_u) => true,
            Property::TopicAliasMaximum(_u) => true,
            Property::ReasonString(_u) => true,
            Property::UserProperty(_u) => true,
            Property::WildcardSubscriptionAvailable(_u) => true,
            Property::SubscriptionIdentifierAvailable(_u) => true,
            Property::SharedSubscriptionAvailable(_u) => true,
            Property::ServerKeepAlive(_u) => true,
            Property::ResponseInformation(_u) => true,
            Property::ServerReference(_u) => true,
            Property::AuthenticationMethod(_u) => true,
            Property::AuthenticationData(_u) => true,
            _ => false,
        }
    }

    pub fn publish_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::PayloadFormat(_u) => true,
            Property::MessageExpiryInterval(_u) => true,
            Property::TopicAlias(_u) => true,
            Property::ResponseTopic(_u) => true,
            Property::CorrelationData(_u) => true,
            Property::UserProperty(_u) => true,
            Property::SubscriptionIdentifier(_u) => true,
            Property::ContentType(_u) => true,
            _ => false,
        }
    }

    pub fn puback_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::ReasonString(_u) => true,
            Property::UserProperty(_u) => true,
            _ => false,
        }
    }

    pub fn pubrec_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::ReasonString(_u) => true,
            Property::UserProperty(_u) => true,
            _ => false,
        }
    }

    pub fn pubrel_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::ReasonString(_u) => true,
            Property::UserProperty(_u) => true,
            _ => false,
        }
    }

    pub fn pubcomp_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::ReasonString(_u) => true,
            Property::UserProperty(_u) => true,
            _ => false,
        }
    }

    pub fn subscribe_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::SubscriptionIdentifier(_u) => true,
            Property::UserProperty(_u) => true,
            _ => false,
        }
    }

    pub fn suback_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::ReasonString(_u) => true,
            Property::UserProperty(_u) => true,
            _ => false,
        }
    }

    pub fn unsubscribe_property(&self) -> bool {
        matches!(self, Property::UserProperty(_u))
    }

    pub fn unsuback_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::ReasonString(_u) => true,
            Property::UserProperty(_u) => true,
            _ => false,
        }
    }

    pub fn pingreq_property(&self) -> bool {
        warn!("pingreq property list is incomplete");
        false
    }

    pub fn pingresp_property(&self) -> bool {
        warn!("pingresp property list is incomplete");
        false
    }

    pub fn disconnect_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::SessionExpiryInterval(_u) => true,
            Property::ReasonString(_u) => true,
            Property::UserProperty(_u) => true,
            Property::ServerReference(_u) => true,
            _ => false,
        }
    }

    pub fn auth_property(&self) -> bool {
        // not possible to use with associated values with different types
        #[allow(clippy::match_like_matches_macro)]
        match self {
            Property::AuthenticationMethod(_u) => true,
            Property::AuthenticationData(_u) => true,
            Property::ReasonString(_u) => true,
            Property::UserProperty(_u) => true,
            _ => false,
        }
    }

    pub fn encoded_len(&self) -> u16 {
        match self {
            Property::PayloadFormat(_u) => 1,
            Property::MessageExpiryInterval(_u) => 4,
            Property::ContentType(u) => u.encoded_len(),
            Property::ResponseTopic(u) => u.encoded_len(),
            Property::CorrelationData(u) => u.encoded_len(),
            Property::SubscriptionIdentifier(u) => {
                VariableByteIntegerEncoder::len(VariableByteIntegerEncoder::encode(*u).unwrap())
                    as u16
            }
            Property::SessionExpiryInterval(_u) => 4,
            Property::AssignedClientIdentifier(u) => u.encoded_len(),
            Property::ServerKeepAlive(_u) => 2,
            Property::AuthenticationMethod(u) => u.encoded_len(),
            Property::AuthenticationData(u) => u.encoded_len(),
            Property::RequestProblemInformation(_u) => 1,
            Property::WillDelayInterval(_u) => 4,
            Property::RequestResponseInformation(_u) => 1,
            Property::ResponseInformation(u) => u.encoded_len(),
            Property::ServerReference(u) => u.encoded_len(),
            Property::ReasonString(u) => u.encoded_len(),
            Property::ReceiveMaximum(_u) => 2,
            Property::TopicAliasMaximum(_u) => 2,
            Property::TopicAlias(_u) => 2,
            Property::MaximumQoS(_u) => 1,
            Property::RetainAvailable(_u) => 1,
            Property::UserProperty(u) => u.encoded_len(),
            Property::MaximumPacketSize(_u) => 4,
            Property::WildcardSubscriptionAvailable(_u) => 1,
            Property::SubscriptionIdentifierAvailable(_u) => 1,
            Property::SharedSubscriptionAvailable(_u) => 1,
            _ => 0,
        }
    }

    pub fn encode(&self, buff_writer: &mut BuffWriter<'a>) -> Result<(), BufferError> {
        match self {
            Property::PayloadFormat(u) => buff_writer.write_u8(*u),
            Property::MessageExpiryInterval(u) => buff_writer.write_u32(*u),
            Property::ContentType(u) => buff_writer.write_string_ref(u),
            Property::ResponseTopic(u) => buff_writer.write_string_ref(u),
            Property::CorrelationData(u) => buff_writer.write_binary_ref(u),
            Property::SubscriptionIdentifier(u) => buff_writer.write_variable_byte_int(*u),
            Property::SessionExpiryInterval(u) => buff_writer.write_u32(*u),
            Property::AssignedClientIdentifier(u) => buff_writer.write_string_ref(u),
            Property::ServerKeepAlive(u) => buff_writer.write_u16(*u),
            Property::AuthenticationMethod(u) => buff_writer.write_string_ref(u),
            Property::AuthenticationData(u) => buff_writer.write_binary_ref(u),
            Property::RequestProblemInformation(u) => buff_writer.write_u8(*u),
            Property::WillDelayInterval(u) => buff_writer.write_u32(*u),
            Property::RequestResponseInformation(u) => buff_writer.write_u8(*u),
            Property::ResponseInformation(u) => buff_writer.write_string_ref(u),
            Property::ServerReference(u) => buff_writer.write_string_ref(u),
            Property::ReasonString(u) => buff_writer.write_string_ref(u),
            Property::ReceiveMaximum(u) => buff_writer.write_u16(*u),
            Property::TopicAliasMaximum(u) => buff_writer.write_u16(*u),
            Property::TopicAlias(u) => buff_writer.write_u16(*u),
            Property::MaximumQoS(u) => buff_writer.write_u8(*u),
            Property::RetainAvailable(u) => buff_writer.write_u8(*u),
            Property::UserProperty(u) => buff_writer.write_string_pair_ref(u),
            Property::MaximumPacketSize(u) => buff_writer.write_u32(*u),
            Property::WildcardSubscriptionAvailable(u) => buff_writer.write_u8(*u),
            Property::SubscriptionIdentifierAvailable(u) => buff_writer.write_u8(*u),
            Property::SharedSubscriptionAvailable(u) => buff_writer.write_u8(*u),
            _ => Err(BufferError::PropertyNotFound),
        }
    }

    pub fn decode(buff_reader: &mut BuffReader<'a>) -> Result<Property<'a>, BufferError> {
        let property_identifier = buff_reader.read_u8();
        return match property_identifier {
            Ok(0x01) => Ok(Property::PayloadFormat(buff_reader.read_u8()?)),
            Ok(0x02) => Ok(Property::MessageExpiryInterval(buff_reader.read_u32()?)),
            Ok(0x03) => Ok(Property::ContentType(buff_reader.read_string()?)),
            Ok(0x08) => Ok(Property::ResponseTopic(buff_reader.read_string()?)),
            Ok(0x09) => Ok(Property::CorrelationData(buff_reader.read_binary()?)),
            Ok(0x0B) => Ok(Property::SubscriptionIdentifier(
                buff_reader.read_variable_byte_int()?,
            )),
            Ok(0x11) => Ok(Property::SessionExpiryInterval(buff_reader.read_u32()?)),
            Ok(0x12) => Ok(Property::AssignedClientIdentifier(
                buff_reader.read_string()?,
            )),
            Ok(0x13) => Ok(Property::ServerKeepAlive(buff_reader.read_u16()?)),
            Ok(0x15) => Ok(Property::AuthenticationMethod(buff_reader.read_string()?)),
            Ok(0x16) => Ok(Property::AuthenticationData(buff_reader.read_binary()?)),
            Ok(0x17) => Ok(Property::RequestProblemInformation(buff_reader.read_u8()?)),
            Ok(0x18) => Ok(Property::WillDelayInterval(buff_reader.read_u32()?)),
            Ok(0x19) => Ok(Property::RequestResponseInformation(buff_reader.read_u8()?)),
            Ok(0x1A) => Ok(Property::ResponseInformation(buff_reader.read_string()?)),
            Ok(0x1C) => Ok(Property::ServerReference(buff_reader.read_string()?)),
            Ok(0x1F) => Ok(Property::ReasonString(buff_reader.read_string()?)),
            Ok(0x21) => Ok(Property::ReceiveMaximum(buff_reader.read_u16()?)),
            Ok(0x22) => Ok(Property::TopicAliasMaximum(buff_reader.read_u16()?)),
            Ok(0x23) => Ok(Property::TopicAlias(buff_reader.read_u16()?)),
            Ok(0x24) => Ok(Property::MaximumQoS(buff_reader.read_u8()?)),
            Ok(0x25) => Ok(Property::RetainAvailable(buff_reader.read_u8()?)),
            Ok(0x26) => Ok(Property::UserProperty(buff_reader.read_string_pair()?)),
            Ok(0x27) => Ok(Property::MaximumPacketSize(buff_reader.read_u32()?)),
            Ok(0x28) => Ok(Property::WildcardSubscriptionAvailable(
                buff_reader.read_u8()?,
            )),
            Ok(0x29) => Ok(Property::SubscriptionIdentifierAvailable(
                buff_reader.read_u8()?,
            )),
            Ok(0x2A) => Ok(Property::SharedSubscriptionAvailable(
                buff_reader.read_u8()?,
            )),
            Err(err) => Err(err),
            _ => Err(BufferError::IdNotFound),
        };
    }
}

impl<'a> From<&Property<'a>> for u8 {
    fn from(value: &Property<'a>) -> Self {
        match value {
            Property::PayloadFormat(_u) => 0x01,
            Property::MessageExpiryInterval(_u) => 0x02,
            Property::ContentType(_u) => 0x03,
            Property::ResponseTopic(_u) => 0x08,
            Property::CorrelationData(_u) => 0x09,
            Property::SubscriptionIdentifier(_u) => 0x0B,
            Property::SessionExpiryInterval(_u) => 0x11,
            Property::AssignedClientIdentifier(_u) => 0x12,
            Property::ServerKeepAlive(_u) => 0x13,
            Property::AuthenticationMethod(_u) => 0x15,
            Property::AuthenticationData(_u) => 0x16,
            Property::RequestProblemInformation(_u) => 0x17,
            Property::WillDelayInterval(_u) => 0x18,
            Property::RequestResponseInformation(_u) => 0x19,
            Property::ResponseInformation(_u) => 0x1A,
            Property::ServerReference(_u) => 0x1C,
            Property::ReasonString(_u) => 0x1F,
            Property::ReceiveMaximum(_u) => 0x21,
            Property::TopicAliasMaximum(_u) => 0x22,
            Property::TopicAlias(_u) => 0x23,
            Property::MaximumQoS(_u) => 0x24,
            Property::RetainAvailable(_u) => 0x25,
            Property::UserProperty(_u) => 0x26,
            Property::MaximumPacketSize(_u) => 0x27,
            Property::WildcardSubscriptionAvailable(_u) => 0x28,
            Property::SubscriptionIdentifierAvailable(_u) => 0x29,
            Property::SharedSubscriptionAvailable(_u) => 0x2A,
            _ => 0x00,
        }
    }
}

impl<'a> From<u8> for Property<'a> {
    fn from(_orig: u8) -> Self {
        warn!("Deserialization of Properties from u8 is not implemented");
        Property::Reserved()
    }
}