dcs2 0.1.0

An extensible distributed control system framework made in rust with no-std support.
Documentation
//! # DCS Communication module
//! Defines the interfaces for receiving and sending messages, for a reading and writting from
//! a connection and how to create connections.
//!
//! The common messages exchanged by the nodes are also included as well as interfaces to serialize
//! and deserialize them; a generic implementation for a message codifier is offered. Messages are
//! wrapped in packages that can be constructed using builders whose interfaces are provided here.
//!
//! For routing messages, i.e. translating from a low level addres to the system wide [`crate::nodes::SystemNodeId`]
//! an interface is provided.
//!
//! Finally a generic implementation of a communication service used for sending and receiving bytes
//! is given. Said bytes can be decoded using the aforementioned message codifiers.
use core::fmt::{Debug, Display, Formatter};

pub mod connection;
pub mod messages;
pub mod router;
pub mod service;

use crate::CodificationError;
use crate::properties::DESCRIPTIVE_LOG_ERROR_MAX_SIZE;

/// Generic error that displays a custom message.
#[derive(Debug)]
pub struct DescriptiveError {
    buff: [u8; DESCRIPTIVE_LOG_ERROR_MAX_SIZE],
    len: usize,
}

impl Display for DescriptiveError {
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
        f.write_str(core::str::from_utf8(&self.buff.as_slice()[..self.len]).unwrap_or_default())
    }
}

impl DescriptiveError {
    #[cfg(test)]
    pub(crate) fn new(args: &str) -> Self {
        let bytes = args.as_bytes();
        let mut buff = [0_u8; DESCRIPTIVE_LOG_ERROR_MAX_SIZE];
        let len = core::cmp::min(bytes.len(), buff.len());
        buff[..len].copy_from_slice(&bytes[..len]);
        Self { buff, len }
    }
}

impl<E: AsRef<[u8]>> From<E> for DescriptiveError {
    fn from(e: E) -> Self {
        let bytes = e.as_ref();
        let mut buff = [0_u8; DESCRIPTIVE_LOG_ERROR_MAX_SIZE];
        let len = core::cmp::min(bytes.len(), buff.len());
        buff[..len].copy_from_slice(&bytes[..len]);
        Self { buff, len }
    }
}

pub trait Error: Display + Debug + AsRef<[u8]> {}

#[cfg(test)]
mod connection_error_tests {
    extern crate alloc;
    extern crate std;

    use self::alloc::string::ToString;
    use alloc::format;
    use core::fmt::{Debug, Display, Formatter};

    use crate::communication::DescriptiveError;

    #[test]
    fn can_create_error_from_str() {
        let error = DescriptiveError::new("custom message");
        assert_eq!("custom message", format!("{}", error))
    }

    #[test]
    fn can_create_error_from_error() {
        let original_error = MockError;
        let error = DescriptiveError::from(&original_error);
        assert_eq!("mock error message", format!("{}", error))
    }

    #[test]
    fn can_create_error_from_io_error() {
        let io_error = std::io::Error::new(std::io::ErrorKind::BrokenPipe, MockError);
        let error = DescriptiveError::from(&io_error.to_string());
        assert_eq!("mock error message", format!("{}", error))
    }

    struct MockError;

    impl Debug for MockError {
        fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
            f.write_str("mock error message")
        }
    }

    impl Display for MockError {
        fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
            f.write_str("mock error message")
        }
    }

    impl std::error::Error for MockError {}

    impl AsRef<[u8]> for MockError {
        fn as_ref(&self) -> &'static [u8] {
            "mock error message".as_bytes()
        }
    }
}

/// Defines errors encountered while sending and receiving data through a connection
#[non_exhaustive]
#[derive(Debug)]
pub enum ConnectionError {
    IOError(DescriptiveError),
    ReadError(ReadError),
    WriteError(WriteError),
    PoolFullError,
    CreationError,
    NotSupported,
    NotAvailable,
    ShouldBlock,
    NotConnected
}

#[allow(clippy::large_enum_variant)]
#[non_exhaustive]
#[derive(Debug)]
pub enum ReadError {
    UnknownError,
    IOError(DescriptiveError),
}

#[allow(clippy::large_enum_variant)]
#[non_exhaustive]
#[derive(Debug)]
pub enum WriteError {
    CodificationError(CodificationError),
    IOError(DescriptiveError),
    UnknownError,
}