coap-zero 0.3.0

CoAP protocol implementation for no_std without alloc
Documentation
// Copyright Open Logistics Foundation
//
// Licensed under the Open Logistics Foundation License 1.3.
// For details on the licensing terms, see the LICENSE file.
// SPDX-License-Identifier: OLFL-1.3

//! Codes (Response and Request) for CoAP Messages
//!
//! All codes according to <https://www.rfc-editor.org/rfc/rfc7252> are supported.

use super::Error;
use bondrewd::Bitfields;

/// Code as it is used in an EncodedMessage
#[derive(Bitfields, Debug, PartialEq, Eq, Copy, Clone)]
#[bondrewd(default_endianness = "big", enforce_bytes = 1)]
pub(crate) struct EncodedCode {
    #[bondrewd(bit_length = 3)]
    pub(crate) class: u8,
    #[bondrewd(bit_length = 5)]
    pub(crate) detail: u8,
}

impl From<Code> for EncodedCode {
    fn from(code: Code) -> EncodedCode {
        let (class, detail) = match code {
            Code::Empty => (0, 0),
            Code::Request(code) => match code {
                RequestCode::Get => (0, 1),
                RequestCode::Post => (0, 2),
                RequestCode::Put => (0, 3),
                RequestCode::Delete => (0, 4),
            },
            Code::Response(code) => match code {
                ResponseCode::Success(code) => match code {
                    SuccessCode::Created => (2, 1),
                    SuccessCode::Deleted => (2, 2),
                    SuccessCode::Valid => (2, 3),
                    SuccessCode::Changed => (2, 4),
                    SuccessCode::Content => (2, 5),
                },
                ResponseCode::ClientError(code) => match code {
                    ClientErrorCode::BadRequest => (4, 0),
                    ClientErrorCode::Unauthorized => (4, 1),
                    ClientErrorCode::BadOption => (4, 2),
                    ClientErrorCode::Forbidden => (4, 3),
                    ClientErrorCode::NotFound => (4, 4),
                    ClientErrorCode::MethodNotAllowed => (4, 5),
                    ClientErrorCode::NotAcceptable => (4, 6),
                    ClientErrorCode::PreconditionFailed => (4, 12),
                    ClientErrorCode::RequestEntityTooLarge => (4, 13),
                    ClientErrorCode::UnsupportedContentFormat => (4, 15),
                },
                ResponseCode::ServerError(code) => match code {
                    ServerErrorCode::InternalServerError => (5, 0),
                    ServerErrorCode::NotImplemented => (5, 1),
                    ServerErrorCode::BadGateway => (5, 2),
                    ServerErrorCode::ServiceUnavailable => (5, 3),
                    ServerErrorCode::GatewayTimeout => (5, 4),
                    ServerErrorCode::ProxyingNotSupported => (5, 5),
                },
            },
        };

        EncodedCode { class, detail }
    }
}

/// CoAP Message Codes according to <https://www.rfc-editor.org/rfc/rfc7252>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Code {
    /// 0.00
    Empty,
    /// Any request code (0.0X)
    Request(RequestCode),
    /// Any response code (2.0X, 4.0X, 5.0X)
    Response(ResponseCode),
}

/// Method Codes according to <https://www.rfc-editor.org/rfc/rfc7252#section-5.8>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RequestCode {
    /// 0.01
    Get,
    /// 0.02
    Post,
    /// 0.03
    Put,
    /// 0.04
    Delete,
}

/// Response Codes according to <https://www.rfc-editor.org/rfc/rfc7252#section-5.9>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResponseCode {
    /// 2.0X
    Success(SuccessCode),
    /// 4.0X
    ClientError(ClientErrorCode),
    /// 5.0X
    ServerError(ServerErrorCode),
}

/// Success Codes according to <https://www.rfc-editor.org/rfc/rfc7252#section-5.9.1>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SuccessCode {
    /// 2.01
    Created,
    /// 2.02
    Deleted,
    /// 2.03
    Valid,
    /// 2.04
    Changed,
    /// 2.05
    Content,
}

/// Client Error Codes according to <https://www.rfc-editor.org/rfc/rfc7252#section-5.9.2>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClientErrorCode {
    /// 4.00
    BadRequest,
    /// 4.01
    Unauthorized,
    /// 4.02
    BadOption,
    /// 4.03
    Forbidden,
    /// 4.04
    NotFound,
    /// 4.05
    MethodNotAllowed,
    /// 4.06
    NotAcceptable,
    /// 4.12
    PreconditionFailed,
    /// 4.13
    RequestEntityTooLarge,
    /// 4.15
    UnsupportedContentFormat,
}

/// Server Error Codes according to <https://www.rfc-editor.org/rfc/rfc7252#section-5.9.3>
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ServerErrorCode {
    /// 5.00
    InternalServerError,
    /// 5.01
    NotImplemented,
    /// 5.02
    BadGateway,
    /// 5.03
    ServiceUnavailable,
    /// 5.04
    GatewayTimeout,
    /// 5.05
    ProxyingNotSupported,
}

impl From<RequestCode> for Code {
    fn from(code: RequestCode) -> Code {
        Code::Request(code)
    }
}

impl TryFrom<Code> for RequestCode {
    type Error = ();

    fn try_from(value: Code) -> Result<Self, Self::Error> {
        match value {
            Code::Request(code) => Ok(code),
            _ => Err(()),
        }
    }
}

impl From<ResponseCode> for Code {
    fn from(code: ResponseCode) -> Code {
        Code::Response(code)
    }
}

impl TryFrom<Code> for ResponseCode {
    type Error = ();

    fn try_from(value: Code) -> Result<Self, Self::Error> {
        match value {
            Code::Response(code) => Ok(code),
            _ => Err(()),
        }
    }
}

impl From<SuccessCode> for ResponseCode {
    fn from(code: SuccessCode) -> ResponseCode {
        ResponseCode::Success(code)
    }
}

impl From<SuccessCode> for Code {
    fn from(code: SuccessCode) -> Code {
        ResponseCode::Success(code).into()
    }
}

impl TryFrom<Code> for SuccessCode {
    type Error = ();

    fn try_from(value: Code) -> Result<Self, Self::Error> {
        match value {
            Code::Response(ResponseCode::Success(code)) => Ok(code),
            _ => Err(()),
        }
    }
}

impl From<ClientErrorCode> for ResponseCode {
    fn from(code: ClientErrorCode) -> ResponseCode {
        ResponseCode::ClientError(code)
    }
}

impl From<ClientErrorCode> for Code {
    fn from(code: ClientErrorCode) -> Code {
        ResponseCode::ClientError(code).into()
    }
}

impl TryFrom<Code> for ClientErrorCode {
    type Error = ();

    fn try_from(value: Code) -> Result<Self, Self::Error> {
        match value {
            Code::Response(ResponseCode::ClientError(code)) => Ok(code),
            _ => Err(()),
        }
    }
}

impl From<ServerErrorCode> for ResponseCode {
    fn from(code: ServerErrorCode) -> ResponseCode {
        ResponseCode::ServerError(code)
    }
}

impl From<ServerErrorCode> for Code {
    fn from(code: ServerErrorCode) -> Code {
        ResponseCode::ServerError(code).into()
    }
}

impl TryFrom<Code> for ServerErrorCode {
    type Error = ();

    fn try_from(value: Code) -> Result<Self, Self::Error> {
        match value {
            Code::Response(ResponseCode::ServerError(code)) => Ok(code),
            _ => Err(()),
        }
    }
}

impl TryFrom<EncodedCode> for Code {
    type Error = Error;

    fn try_from(code: EncodedCode) -> Result<Code, Self::Error> {
        let code = match (code.class, code.detail) {
            // Empty
            (0, 0) => Code::Empty,
            // Request Codes
            (0, 1) => RequestCode::Get.into(),
            (0, 2) => RequestCode::Post.into(),
            (0, 3) => RequestCode::Put.into(),
            (0, 4) => RequestCode::Delete.into(),
            // Success Codes
            (2, 1) => SuccessCode::Created.into(),
            (2, 2) => SuccessCode::Deleted.into(),
            (2, 3) => SuccessCode::Valid.into(),
            (2, 4) => SuccessCode::Changed.into(),
            (2, 5) => SuccessCode::Content.into(),
            // Client Error Codes
            (4, 0) => ClientErrorCode::BadRequest.into(),
            (4, 1) => ClientErrorCode::Unauthorized.into(),
            (4, 2) => ClientErrorCode::BadOption.into(),
            (4, 3) => ClientErrorCode::Forbidden.into(),
            (4, 4) => ClientErrorCode::NotFound.into(),
            (4, 5) => ClientErrorCode::MethodNotAllowed.into(),
            (4, 6) => ClientErrorCode::NotAcceptable.into(),
            (4, 12) => ClientErrorCode::PreconditionFailed.into(),
            (4, 13) => ClientErrorCode::RequestEntityTooLarge.into(),
            (4, 15) => ClientErrorCode::UnsupportedContentFormat.into(),
            // Server Error Codes
            (5, 0) => ServerErrorCode::InternalServerError.into(),
            (5, 1) => ServerErrorCode::NotImplemented.into(),
            (5, 2) => ServerErrorCode::BadGateway.into(),
            (5, 3) => ServerErrorCode::ServiceUnavailable.into(),
            (5, 4) => ServerErrorCode::GatewayTimeout.into(),
            (5, 5) => ServerErrorCode::ProxyingNotSupported.into(),
            // Unknown
            (_, _) => return Err(Error::InvalidCode),
        };

        Ok(code)
    }
}