use super::{Handler, SelfMadeHandler};
use crate::client::ClientState;
use crate::names::cmd::{PING, PONG};
use crate::{
client::{
channel::{ChannelSpec, Sender, SenderRef},
queue::QueueEditGuard,
},
ircmsg::{ClientMsg, ServerMsg},
string::Arg,
};
use std::time::Instant;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
pub struct Ping(pub std::time::Instant);
impl Default for Ping {
fn default() -> Self {
Ping(std::time::Instant::now())
}
}
impl Handler for Ping {
type Value = (Option<crate::ircmsg::Source<'static>>, std::time::Duration);
fn handle(
&mut self,
msg: &ServerMsg<'_>,
_: &mut ClientState,
_: QueueEditGuard<'_>,
mut channel: SenderRef<'_, Self::Value>,
) -> std::ops::ControlFlow<()> {
if msg.kind == PONG {
if let Some(last) = msg.args.split_last().1 {
let hash = crate::util::mangle(&self.0);
let mut value: u32 = 0;
for byte in last.as_bytes().iter().cloned() {
if !(b'0'..=b'7').contains(&byte) {
return std::ops::ControlFlow::Continue(());
}
value <<= 3;
value |= (byte - b'0') as u32;
}
if hash == value {
let duration = Instant::now().saturating_duration_since(self.0);
let source = msg.source.clone().map(crate::ircmsg::SharedSource::owning_merged);
channel.send((source, duration));
return std::ops::ControlFlow::Break(());
}
}
}
std::ops::ControlFlow::Continue(())
}
}
impl SelfMadeHandler for Ping {
type Receiver<Spec: ChannelSpec> = Spec::Oneshot<Self::Value>;
fn queue_msgs(&self, _: &ClientState, mut queue: QueueEditGuard<'_>) {
let mut msg = ClientMsg::new(PING);
let hash = crate::util::mangle(&self.0);
let hash: Arg<'static> = format!("{hash:o}").try_into().unwrap();
msg.args.edit().add_word(hash);
queue.push(msg);
}
fn make_channel<Spec: ChannelSpec>(
spec: &Spec,
) -> (Box<dyn Sender<Value = Self::Value> + Send>, Self::Receiver<Spec>) {
spec.new_oneshot()
}
}
pub(crate) fn pong(
msg: &ServerMsg<'_>,
mut queue: impl crate::client::ClientMsgSink<'static>,
) -> bool {
let retval = msg.kind == PING;
if retval {
let mut reply = ClientMsg::new(PONG);
if let Some(last) = msg.args.split_last().1 {
reply.args.edit().add(last.clone().owning());
}
queue.send(reply);
}
retval
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
pub struct AutoPong;
impl Handler for AutoPong {
type Value = ();
fn handle(
&mut self,
msg: &ServerMsg<'_>,
_: &mut ClientState,
mut queue: QueueEditGuard<'_>,
_: SenderRef<'_, Self::Value>,
) -> std::ops::ControlFlow<()> {
pong(msg, &mut queue);
std::ops::ControlFlow::Continue(())
}
}
impl SelfMadeHandler for AutoPong {
type Receiver<Spec: ChannelSpec> = ();
fn queue_msgs(&self, _: &ClientState, _: QueueEditGuard<'_>) {}
fn make_channel<Spec: ChannelSpec>(
_: &Spec,
) -> (Box<dyn Sender<Value = Self::Value> + Send>, Self::Receiver<Spec>) {
(Box::<crate::client::channel::ClosedSender<_>>::default(), ())
}
}