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<()> {
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(())
}
}