pub mod server; use iced::futures; use iced::subscription::{self, Subscription}; use futures::channel::mpsc; use futures::sink::SinkExt; use futures::stream::StreamExt; use async_tungstenite::tungstenite; use std::fmt; pub fn connect() -> Subscription { struct Connect; subscription::unfold( std::any::TypeId::of::(), State::Disconnected, |state| async move { match state { State::Disconnected => { const ECHO_SERVER: &str = "ws://localhost:3030"; match async_tungstenite::tokio::connect_async(ECHO_SERVER) .await { Ok((websocket, _)) => { let (sender, receiver) = mpsc::channel(100); ( Some(Event::Connected(Connection(sender))), State::Connected(websocket, receiver), ) } Err(_) => { tokio::time::sleep( tokio::time::Duration::from_secs(1), ) .await; (Some(Event::Disconnected), State::Disconnected) } } } State::Connected(mut websocket, mut input) => { let mut fused_websocket = websocket.by_ref().fuse(); futures::select! { received = fused_websocket.select_next_some() => { match received { Ok(tungstenite::Message::Text(message)) => { ( Some(Event::MessageReceived(Message::User(message))), State::Connected(websocket, input) ) } Ok(_) => { (None, State::Connected(websocket, input)) } Err(_) => { (Some(Event::Disconnected), State::Disconnected) } } } message = input.select_next_some() => { let result = websocket.send(tungstenite::Message::Text(message.to_string())).await; if result.is_ok() { (None, State::Connected(websocket, input)) } else { (Some(Event::Disconnected), State::Disconnected) } } } } } }, ) } #[derive(Debug)] #[allow(clippy::large_enum_variant)] enum State { Disconnected, Connected( async_tungstenite::WebSocketStream< async_tungstenite::tokio::ConnectStream, >, mpsc::Receiver, ), } #[derive(Debug, Clone)] pub enum Event { Connected(Connection), Disconnected, MessageReceived(Message), } #[derive(Debug, Clone)] pub struct Connection(mpsc::Sender); impl Connection { pub fn send(&mut self, message: Message) { self.0 .try_send(message) .expect("Send message to echo server"); } } #[derive(Debug, Clone)] pub enum Message { Connected, Disconnected, User(String), } impl Message { pub fn new(message: &str) -> Option { if message.is_empty() { None } else { Some(Self::User(message.to_string())) } } pub fn connected() -> Self { Message::Connected } pub fn disconnected() -> Self { Message::Disconnected } } impl fmt::Display for Message { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Message::Connected => write!(f, "Connected successfully!"), Message::Disconnected => { write!(f, "Connection lost... Retrying...") } Message::User(message) => write!(f, "{message}"), } } }