use std::{
cell::{Cell, RefCell},
rc::Rc,
sync::Arc,
};
use gled_protocol::{
action::route_actions_on_this_thread,
message::{ClientMessage, ServerMessage, Subscriptions},
state::{ClientInput, route_inputs_on_this_thread},
};
use web_time::{Duration, Instant};
const SILENCE: Duration = Duration::from_secs(3);
pub trait EngineLink {
fn send(&mut self, message: ClientMessage);
fn receive(&mut self) -> Vec<ServerMessage>;
fn is_remote(&self) -> bool {
false
}
fn is_open(&self) -> bool {
true
}
fn target(&self) -> Option<String> {
None
}
fn background_sender(&self) -> Option<BackgroundSender> {
None
}
}
pub type BackgroundSender = Arc<dyn Fn(ClientMessage) + Send + Sync>;
#[derive(Debug, Clone, Copy)]
pub(crate) struct Interruption {
pub(crate) last_contact: Duration,
}
#[derive(Clone)]
pub struct Connection {
link: Rc<RefCell<Box<dyn EngineLink>>>,
sequence: Rc<Cell<u64>>,
last_contact: Rc<Cell<Option<Instant>>>,
}
impl std::fmt::Debug for Connection {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Connection")
.field("sequence", &self.sequence.get())
.finish_non_exhaustive()
}
}
impl Connection {
pub fn new(link: Box<dyn EngineLink>, subscriptions: Subscriptions) -> Self {
let connection = Self {
link: Rc::new(RefCell::new(link)),
sequence: Rc::default(),
last_contact: Rc::default(),
};
connection.send(ClientMessage::Subscribe(subscriptions));
let actions = connection.clone();
route_actions_on_this_thread(move |action| {
let sequence = actions.sequence.get() + 1;
actions.sequence.set(sequence);
actions.send(ClientMessage::Input(ClientInput::Action {
sequence,
action,
}));
});
let inputs = connection.clone();
route_inputs_on_this_thread(move |input| inputs.send(ClientMessage::Input(input)));
connection
}
pub(crate) fn send(&self, message: ClientMessage) {
match self.link.try_borrow_mut() {
Ok(mut link) => link.send(message),
Err(_) => tracing::warn!("Dropped {message:?}: the link is busy"),
}
}
pub(crate) fn receive(&self) -> Vec<ServerMessage> {
let mut link = self.link.borrow_mut();
let messages = link.receive();
if !messages.is_empty() && link.is_open() {
self.last_contact.set(Some(Instant::now()));
}
messages
}
pub(crate) fn is_remote(&self) -> bool {
self.link.borrow().is_remote()
}
pub(crate) fn target(&self) -> Option<String> {
self.link.borrow().target()
}
pub(crate) fn background_sender(&self) -> Option<BackgroundSender> {
self.link.borrow().background_sender()
}
pub(crate) fn interruption(&self) -> Option<Interruption> {
let link = self.link.borrow();
let last_contact = self.last_contact.get()?.elapsed();
(link.is_remote() && (!link.is_open() || last_contact > SILENCE))
.then_some(Interruption { last_contact })
}
pub(crate) fn sent_sequence(&self) -> u64 {
self.sequence.get()
}
pub(crate) fn reset_sequence(&self) {
self.sequence.set(0);
}
}