nardol 0.0.3

Simple framework that provides structure to data sent and received from network.
Documentation
mod content_type;
// mod defaults;
mod metadata_type;
// HOW THIS WILL BE ACCESSED???
/// Utility functions that can be used when implementing a Message.
pub mod utils;

use std::fmt::Debug;
use std::net::{SocketAddr, TcpStream, ToSocketAddrs, UdpSocket};

use crate::error::Error;
use crate::packet::Packet;
use crate::ron::{FromRon, ToRon};

pub use content_type::ContentType;
pub use metadata_type::MetaDataType;

type SendContextOption<'a, M, C> = Option<<M as MetaDataType<'a, M, C>>::SendContext>;
type SendOutputOption<'a, M, C> = Option<<C as ContentType<'a, M, C>>::SendOutput>;
type ReceiveContextOption<'a, M, C> = Option<<M as MetaDataType<'a, M, C>>::ReceiveContext>;
type ReceiveOutputOption<'a, M, C> = Option<<C as ContentType<'a, M, C>>::ReceiveOutput>;

/// Trait that allows implementor to send and receive itself via [TcpStream]
/// with provided [TcpMessage::send] and [TcpMessage::receive].
pub trait TcpMessage<'a, M, C>
where
    Self: Default,
    M: MetaDataType<'a, M, C>,
    C: ContentType<'a, M, C>,
{
    /// Breaks `self` into `metadata`, `content` and `end_data`.
    fn destructure(self) -> (M, C, Packet);

    /// Builds `Self` from `metadata`, `content` and `end_data`.
    fn build(metadata: M, content: C, end_data: Packet) -> Self;

    /// Sends `Self` via [`stream`](std::net::TcpStream).
    ///
    /// Returns an [optional](Option) `output` which is [ContentType::SendOutput] on
    /// associated [C](ContentType) if successful or an [Error] if not.
    ///
    /// # Arguments
    ///
    /// * `stream` -- [TcpStream] that is used to send a message.
    /// * `context` -- [Optional](Option) `context` to provide additional data if necessary which
    /// has same type as [MetaDataType::SendContext] on associated [M][MetaDataType].
    ///
    /// This uses `send` methods implemented by `metadata` and `content`.
    fn send(
        self,
        stream: &mut TcpStream,
        context: SendContextOption<'a, M, C>,
    ) -> Result<SendOutputOption<'a, M, C>, Error> {
        let (metadata, content, end_data) = self.destructure();
        let ctx = metadata.send(stream, context)?;
        let output = content.send(stream, ctx)?;
        end_data.send(stream)?;

        Ok(output)
    }

    /// Receives a `message` on [`stream`](std::net::TcpStream).
    ///
    /// Returns a [tuple] of `Self` and an [optional](Option) `output` which is
    /// [ContentType::ReceiveOutput] on associated [C](ContentType) if successful or an 
    /// [Error] if not.
    ///
    /// # Arguments
    ///
    /// * `stream` -- [TcpStream] that is used to receive a message on.
    /// * `context` -- [Optional](Option) `context` to provide additional data if necessary which
    /// has same type as [MetaDataType::ReceiveContext] on associated [M][MetaDataType].
    fn receive(
        stream: &mut TcpStream,
        context: ReceiveContextOption<'a, M, C>,
    ) -> Result<(Self, ReceiveOutputOption<'a, M, C>), Error> {
        let (metadata, ctx) = M::receive(stream, &context)?;
        // Since end of content can and should be marked with an end_data,
        // it needs to be returned with it.
        let (content, end_data, output) = C::receive(stream, &metadata, ctx)?;

        let message = Self::build(metadata, content, end_data);

        Ok((message, output))
    }
}

/// Trait that allows implementor to send and receive itself on [UdpSocket] with provided methods.
pub trait UdpMessage<'a>
where
    Self: Default + ToRon + FromRon<'a> + TryInto<Packet> + TryFrom<Packet>,
    Error: From<<Self as TryInto<Packet>>::Error> + From<<Self as TryFrom<Packet>>::Error>,
    <Self as TryInto<Packet>>::Error: Into<Error> + Debug,
    <Self as TryFrom<Packet>>::Error: Into<Error> + Debug,
{
    /// Sends `self` to given `address` on [UdpSocket].
    fn send_to<A>(self, socket: UdpSocket, addr: A) -> Result<(), Error>
    where
        A: ToSocketAddrs,
    {
        let packet: Packet = self.try_into()?;
        packet.send_to(socket, addr)?;

        Ok(())
    }

    /// Receives a `Self` and a [SocketAddr] on a [UdpSocket].
    fn receive_from(socket: UdpSocket) -> Result<(Self, SocketAddr), Error> {
        let (packet, address) = Packet::receive_from(socket)?;
        let message = Self::try_from(packet)?;

        Ok((message, address))
    }

    /// Peeks on data on `socket`, if valid data are received a `Self` is created and
    /// returned together with a [SocketAddr].
    ///
    /// Since `UdpSocket::peek_from` is used to retrieve data,
    /// calling this again returns the same data.
    fn peek_from(socket: UdpSocket) -> Result<(Self, SocketAddr), Error> {
        let (packet, address) = Packet::peek_from(socket)?;
        let message = Self::try_from(packet)?;

        Ok((message, address))
    }

    /// Sends `self` to connected [UdpSocket].
    fn send_to_connected(self, socket: UdpSocket) -> Result<(), Error> {
        let packet: Packet = self.try_into()?;
        packet.send_to_connected(socket)?;

        Ok(())
    }

    /// Receives a `Self` on a connected [UdpSocket].
    fn receive_from_connected(socket: UdpSocket) -> Result<Self, Error> {
        let packet = Packet::receive_from_connected(socket).unwrap();
        let message = Self::try_from(packet)?;

        Ok(message)
    }

    /// Peeks on data on a connected `socket`, if valid data are received a `Self` is created.
    ///
    /// Since `UdpSocket::peek` is used to retrieve data, calling this again returns the same data.
    fn peek_from_connected(socket: UdpSocket) -> Result<Self, Error> {
        let packet = Packet::peek_from_connected(socket)?;
        let message = Self::try_from(packet)?;

        Ok(message)
    }
}

/// Trait that marks data as `Context`,
/// that can be used inside [MetaDataType] and [ContentType] methods.
pub trait ContextType {}

/// Trait that marks data as `Output`,
/// that can be used inside [MetaDataType] and [ContentType] methods.
pub trait OutputType {}