use yew::worker::*;
use crate::coapwsmessage::{CoAPWSMessageR, CoAPWSMessageW};
use coap_message::{MinimalWritableMessage, ReadableMessage};
use coap_handler_implementations::option_processing::OptionsExt;
pub enum Input {
Initialize { uri: String },
Message(CoAPWSMessageW),
}
#[derive(Debug)]
pub enum Output {
Connected,
Message(CoAPWSMessageR<Box<[u8]>>),
SignalingInfo(CoAPWSMessageR<Box<[u8]>>),
Error(OutputError)
}
#[derive(Debug)]
#[non_exhaustive]
pub enum OutputError {
ConnectError(String),
ParseError(&'static str),
TextReceived(String),
SignalingError(CoAPWSMessageR<Box<[u8]>>),
Closed,
SocketError,
}
impl From<OutputError> for Output {
fn from(e: OutputError) -> Output {
Output::Error(e)
}
}
pub struct Connection {
tasks: std::collections::HashMap<HandlerId, crate::ws::Websocket>,
link: AgentLink<Self>,
}
impl Agent for Connection {
type Reach = Context<Self>;
type Message = (HandlerId, WSMsg);
type Input = Input;
type Output = Output;
fn create(
link: AgentLink<Self>,
) -> Self {
let tasks = Default::default();
Connection {
tasks,
link,
}
}
fn disconnected(&mut self, id: HandlerId) {
drop(self.tasks.remove(&id));
}
fn update(&mut self, (handler, msg): (HandlerId, WSMsg)) {
let Some(socket) = self.tasks.get_mut(&handler) else {
return;
};
match msg {
WSMsg::Opened => {
let mut empty_csm = CoAPWSMessageW::new(&[]);
empty_csm.set_code(coap_numbers::code::CSM);
if socket.send_binary(&empty_csm.serialize()).is_err() {
self.link.respond(handler, OutputError::SocketError.into());
self.tasks.remove(&handler);
return;
}
self.link.respond(handler, Output::Connected);
}
WSMsg::Binary(data) => {
let msg = CoAPWSMessageR::new(data.into_boxed_slice());
let msg = match msg {
Err(e) => {
self.link.respond(handler, OutputError::ParseError(e).into());
self.tasks.remove(&handler);
return;
}
Ok(m) => m,
};
if coap_numbers::code::classify(msg.code()) == coap_numbers::code::Range::Signaling {
match msg.code() {
coap_numbers::code::CSM | coap_numbers::code::RELEASE => {
let opts = msg.options().ignore_elective_others();
if opts.is_err() {
self.link.respond(handler, OutputError::SignalingError(msg).into());
self.tasks.remove(&handler);
return;
}
},
coap_numbers::code::PING => {
let mut pong = CoAPWSMessageW::new(msg.token());
pong.set_code(coap_numbers::code::PONG);
let opts = msg.options().ignore_elective_others();
if opts.is_err() {
self.link.respond(handler, OutputError::SignalingError(msg).into());
self.tasks.remove(&handler);
return;
}
if socket.send_binary(&pong.serialize()).is_err() {
self.link.respond(handler, OutputError::SocketError.into());
self.tasks.remove(&handler);
return;
}
}
_ => {
self.link.respond(handler, OutputError::SignalingError(msg).into());
self.tasks.remove(&handler);
return;
},
}
self.link.respond(handler, Output::SignalingInfo(msg));
return;
}
self.link.respond(handler, Output::Message(msg));
}
WSMsg::Close => {
self.link.respond(handler, OutputError::Closed.into());
self.tasks.remove(&handler);
}
WSMsg::Error => {
self.link.respond(handler, OutputError::SocketError.into());
self.tasks.remove(&handler);
}
}
}
fn handle_input(&mut self, input: Input, handler: HandlerId) {
match input {
Input::Initialize { uri } => {
let on_message = self.link.callback(move |m| (handler, WSMsg::Binary(m)));
let on_open = self.link.callback(move |()| (handler, WSMsg::Opened));
let on_error = self.link.callback(move |()| (handler, WSMsg::Error));
let on_close = self.link.callback(move |()| (handler, WSMsg::Close));
let Ok(socket) = crate::ws::Websocket::new(&uri, "coap",
move |msg| on_message.emit(msg),
move || on_open.emit(()),
move || on_error.emit(()),
move || on_close.emit(()),
) else {
self.link.send_message((handler, WSMsg::Error));
return;
};
let old = self.tasks.insert(handler, socket);
debug_assert!(old.is_none(), "Double initialization message");
}
Input::Message(msg) => {
let Some(socket) = self.tasks.get_mut(&handler) else {
return;
};
if socket.send_binary(&msg.serialize()).is_err() {
self.link.send_message((handler, WSMsg::Error));
};
}
}
}
}
#[derive(Debug)]
#[doc(hidden)]
pub enum WSMsg {
Opened,
Error,
Close,
Binary(Vec<u8>),
}