cig 0.1.2

Simplify TCP/IP applications with a transparential, persistent-mode and data-driven protocol
Documentation
use std::net::ToSocketAddrs;
use std::{io, time::SystemTime};

use serde::{de::DeserializeOwned, Serialize};

use crate::{
    pkg::{Pkg, PkgData},
    stog,
};
use crate::{PeerID, IO_RESTORE_CONNECT_SYNC_SECS};

use self::{client::Client, host::Host};

pub mod client;
pub mod host;

pub enum Conn<T: DeserializeOwned + Serialize> {
    Host(Host<T>),
    Client(Client<T>),
}

impl<T: DeserializeOwned + Serialize> Conn<T> {
    pub fn connect<A>(addr: A) -> io::Result<Self>
    where
        A: ToSocketAddrs,
    {
        Ok(Self::Client(Client::connect(addr)?))
    }

    pub fn is_host(&self) -> bool {
        matches!(self, Self::Host(_))
    }

    pub fn host<A>(addr: A) -> io::Result<Self>
    where
        A: ToSocketAddrs,
    {
        Ok(Self::Host(Host::bind(addr)?))
    }

    pub fn poll(&mut self) -> stog::Result<Option<Pkg<T>>> {
        let r = match self {
            Self::Host(ref mut h) => h.poll(),
            Self::Client(ref mut c) => c.poll(),
        };
        if matches!(r, Err(stog::Error::ConnClosed)) {
            self.restore_connection()?;
            Ok(None)
        } else {
            r
        }
    }

    pub fn get_id(&self) -> PeerID {
        match self {
            Self::Host(h) => h.id,
            Self::Client(c) => c.id,
        }
    }

    pub fn send_to(&mut self, id: PeerID, data: T) -> stog::Result<Pkg<T>> {
        let pkg = Pkg::new(self.get_id(), PkgData::Data(data, id));
        match self {
            Self::Host(ref mut h) => h.send_to(id, &pkg)?,
            Self::Client(ref mut c) => stog::send(&pkg, &mut c.stream)?,
        };
        Ok(pkg)
    }

    pub fn send_to_all(&mut self, data: T) -> stog::Result<Pkg<T>> {
        let pkg = Pkg::new(self.get_id(), PkgData::GlobalData(data));
        match self {
            Self::Host(ref mut h) => h.send_to_all(&pkg)?,
            Self::Client(ref mut c) => stog::send(&pkg, &mut c.stream)?,
        };
        Ok(pkg)
    }

    pub fn restore_connection(&mut self) -> stog::Result<()> {
        // works by giving a interval for each connected peer to the old server to stabilish a new
        // connection. If that one can't host, the next one tries.
        let client = match self {
            Conn::Client(c) => c,
            Conn::Host(_) => unreachable!(),
        };
        let connected_peers: Vec<_> = client.get_active_peers().collect();
        loop {
            let now = (SystemTime::now().duration_since(std::time::UNIX_EPOCH)).unwrap();
            let i = (now - client.last_host_timestamp).as_secs()
                / IO_RESTORE_CONNECT_SYNC_SECS as u64
                % connected_peers.len() as u64;
            let target = client.peers[connected_peers[i as usize] as usize]
                .as_ref()
                .unwrap();
            let new_conn = if connected_peers[i as usize] == client.id {
                Conn::host(target)
            } else {
                Conn::connect(target)
            };
            match new_conn {
                Ok(s) => {
                    *self = s;
                    break;
                }
                Err(ref e) if e.kind() == std::io::ErrorKind::ConnectionRefused => (),
                Err(e) => panic!("error restoring: {e:?}"),
            }
        }
        Ok(())
    }
}