cig 0.1.2

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

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

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

pub struct Client<T> {
    pub stream: TcpStream,
    pub id: PeerID,
    pub peers: Vec<Option<String>>,
    pub last_host_timestamp: Duration,
    pub _marker: PhantomData<T>,
}

impl<T: DeserializeOwned + Serialize> Client<T> {
    pub fn connect<A>(addr: A) -> io::Result<Self>
    where
        A: ToSocketAddrs,
    {
        let stream = TcpStream::connect(addr)?;
        stream.set_read_timeout(crate::IO_TIMEOUT)?;
        stream.set_write_timeout(crate::IO_TIMEOUT)?;
        let s = Self {
            stream,
            id: 0,                               // unknown yet
            peers: Vec::default(),               // unknown yet
            last_host_timestamp: Duration::ZERO, // unknown yet
            _marker: PhantomData,
        };
        Ok(s)
    }

    pub fn handle_pkg(&mut self, pkg: Pkg<T>) -> stog::Result<Option<Pkg<T>>> {
        if pkg.is_from_server() {
            self.last_host_timestamp = pkg.time;
        }
        match pkg.data {
            PkgData::SetPeer(id) => {
                // println!("setting peer to {id}");
                self.id = id
            }
            PkgData::SetPeers(ps) => {
                self.peers = ps;
            }
            PkgData::Data(_, _) => {
                return Ok(Some(pkg));
            }
            PkgData::GlobalData(_) => return Ok(Some(pkg)),
            _ => unimplemented!(),
        }
        Ok(None)
    }

    pub fn poll(&mut self) -> stog::Result<Option<Pkg<T>>> {
        match stog::read(&mut self.stream) {
            Ok(pkg) => self.handle_pkg(pkg),
            Err(stog::Error::NoPackage) => Ok(None),
            Err(stog::Error::ConnClosed) => Err(stog::Error::ConnClosed),
            Err(e) => panic!("polling error: {e:?}"),
        }
    }

    pub fn get_active_peers(&self) -> impl Iterator<Item = PeerID> + '_ {
        self.peers.iter().enumerate().filter_map(
            |(i, p)| {
                if p.is_some() {
                    Some(i as PeerID)
                } else {
                    None
                }
            },
        )
    }
}