use std::collections::HashMap;
use futures::FutureExt;
use iroh::{Endpoint, SecretKey, endpoint::{self, Connection, RecvStream, presets}};
use crate::internal::TaskWrapper;
const ALPN: &str = "evergreen/0.1.0";
pub enum ClientUpdate {
IncomingPeer(iroh::EndpointId),
NewPeer(iroh::EndpointId),
}
pub type UpdateCallback = fn(ClientUpdate) -> dyn Future<Output = ()>;
impl Client {
pub async fn new(identity: SecretKey, callback: Option<UpdateCallback>) -> Result<Self, iroh::endpoint::BindError> {
let endpoint = Endpoint::builder(presets::N0)
.alpns(vec![ALPN.into()])
.secret_key(identity)
.bind().await?;
let (request_sender, request_receiver) = mpsc::channel::<Request>(128);
let (response_sender, response_receiver) = mpsc::channel::<ClientUpdate>(128);
let token = tokio_util::sync::CancellationToken::new();
let task_handle = tokio::task::spawn(
event_loop(endpoint.clone(), token.clone(), request_receiver, response_sender, callback)
);
let wrapper = TaskWrapper{
cancel_token: token,
task_handle,
incoming_queue: response_receiver,
outgoing_queue: request_sender
};
endpoint.online().await;
Ok(Client{
endpoint,
task: wrapper,
})
}
pub async fn stop_client(self) {
self.task.cancel();
}
}
pub struct Client {
endpoint: Endpoint,
task: TaskWrapper<(), ClientUpdate, Request>
}
use tokio::{pin, sync::mpsc};
use tokio_util::sync::CancellationToken;
enum Request{
AddConnection(endpoint::Connection),
}
impl Client {
#[inline(always)]
pub fn is_running(&self) -> bool {
self.task.is_done()
}
#[inline(always)]
pub async fn poll(&mut self) -> Option<ClientUpdate> {
self.task.recv().await
}
pub async fn connect_to_peer(&self, id: iroh::EndpointId) -> Result<(), endpoint::ConnectError> {
let connection = self.endpoint.connect(id, ALPN.as_bytes()).await?;
self.task.send(Request::AddConnection(connection)).await
.expect("Internal request channel dropped or closed. Client is not longer valid");
Ok(())
}
pub async fn connect_to_room(&self) -> Result<(), ()> {
todo!()
}
}
mod connection_loop;
async fn event_loop(
endpoint: Endpoint,
cancel_token: tokio_util::sync::CancellationToken,
mut incoming_requests: mpsc::Receiver<Request>,
outgoing_updates: mpsc::Sender<ClientUpdate>,
_callback: Option<UpdateCallback>,
){
let mut room_id: Option<u64> = None;
let mut connections = ConnectionList { list: vec![] };
let mut incoming_connections = connection_loop::IncomingList { list: vec![]};
let mut peer_info: HashMap<iroh::EndpointId, PeerInfo> = HashMap::with_capacity(128);
loop {
tokio::select! {
_ = cancel_token.cancelled() => {
return;
},
new_command = incoming_requests.recv() => {
let Some(command) = new_command else {break};
match command {
Request::AddConnection(connection) => {
connections.list.push(connection)
},
}
},
new_connection = endpoint.accept() => {
match new_connection {
None => cancel_token.cancel(),
Some(incoming) => {
let handle = tokio::task::spawn(handle_incoming(endpoint.clone(), incoming));
incoming_connections.list.push(handle);
},
}
},
(incoming_index, status) = &mut incoming_connections => {
incoming_connections.list.remove(incoming_index);
match status {
Err(error) => todo!(),
Ok(_) => {
},
}
}
(connection_index, stream_event) = &mut connections => {
match stream_event {
Err(_error) => {
connections.list.remove(connection_index);
}
Ok(stream) => {
let _ = tokio::task::spawn(handle_packet(endpoint.clone(), stream, outgoing_updates.clone()));
},
}
}
}
}
}
async fn handle_packet(endpoint: Endpoint, mut stream: RecvStream, outoging: mpsc::Sender<ClientUpdate>) {
let Ok(raw_packet) = stream.read_to_end(512).await else {return};
let Ok(packet) = crate::wire::deserialize_packet(&raw_packet) else {return ;};
todo!()
}
struct ConnectionList {
list: Vec<Connection>,
}
impl Future for ConnectionList {
type Output = (usize, Result<endpoint::RecvStream, endpoint::ConnectionError>);
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
for (index, connection) in self.list.iter().enumerate() {
let future = connection.accept_uni();
pin!(future);
if let Poll::Ready(val) = future.poll(cx) {
return Poll::Ready((index, val));
};
}
Poll::Pending
}
}