1use serde::{Deserialize, Serialize};
2use uuid::Uuid;
3
4#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
5pub struct TalkMessage {
6 pub uuid: Uuid,
7 pub username: String,
8 pub text: String,
9 pub room_id: i32,
10 pub unixtime: u64
11}
12
13#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
14pub enum TalkProtocol {
15 JoinRoom { room_id: i32, uuid: Uuid, username: String, unixtime: u64},
17 LeaveRoom { room_id: i32, uuid: Uuid, username: String, unixtime: u64},
18 ChangeName {uuid: Uuid, username: String, old_username: String, unixtime: u64},
19 Fetch { room_id: i32, limit: i64, fetch_before: u64},
20 LocalError { message: String },
21 LocalInformation { message: String },
22
23 UserJoined { uuid: Uuid, username: String, room_id: i32, unixtime: u64 },
25 UserLeft { uuid: Uuid, username: String, room_id: i32, unixtime: u64 },
26 UsernameChanged {uuid: Uuid, username: String, old_username: String, unixtime: u64},
27 History { text: Vec<TalkProtocol> },
28 Error { code: String, message: String },
29
30
31 PostMessage { message: TalkMessage },
33}
34
35impl TalkProtocol {
36 pub fn serialize(&self) -> Result<Vec<u8>, bincode::Error> {
37 bincode::serialize(self)
38 }
39
40 pub fn deserialize(bytes: &[u8]) -> Result<Self, bincode::Error> {
41 bincode::deserialize(bytes)
42 }
43 pub fn to_i16(&self) -> Option<i16> {
44 match self {
45 TalkProtocol::UserJoined {..} => Some(0),
46 TalkProtocol::UserLeft {..} => Some(1),
47 TalkProtocol::UsernameChanged {..} => Some(2),
48 TalkProtocol::Error { .. } => Some(3),
49 TalkProtocol::PostMessage {..} => Some(4),
50 _ => None,
51 }
52 }
53
54 pub fn from_i16(value: i16, room_id: i32, uuid: Uuid, username: String, unixtime: u64, message: String) -> Option<Self> {
55 Some(match value {
56 0 => TalkProtocol::UserJoined { uuid, username, room_id, unixtime },
57 1 => TalkProtocol::UserLeft { uuid, username, room_id, unixtime },
58 2 => TalkProtocol::UsernameChanged { uuid, username, old_username: message, unixtime },
59 3 => TalkProtocol::Error { code: message.clone(), message },
60 4 => TalkProtocol::PostMessage { message: TalkMessage { uuid, username, text: message, room_id, unixtime } },
61 _ => return None,
62 })
63 }
64}
65
66#[cfg(not(target_arch = "wasm32"))]
67pub mod native {
68 use super::*;
69 use futures_channel::mpsc::UnboundedReceiver;
70 use futures_util::{
71 SinkExt, StreamExt,
72 stream::{SplitSink, SplitStream},
73 };
74 use tokio::net::TcpStream;
75 use tokio_tungstenite::tungstenite::Error as WsError;
76 use tokio_tungstenite::{
77 MaybeTlsStream, WebSocketStream, connect_async, tungstenite::Error,
78 tungstenite::protocol::Message,
79 };
80 type WebStream = WebSocketStream<MaybeTlsStream<TcpStream>>;
81
82 pub async fn connect(
83 url: String,
84 ) -> Result<(SplitSink<WebStream, Message>, SplitStream<WebStream>), Error> {
85 let stream = connect_async(url).await?.0;
86 Ok(stream.split())
87 }
88
89 pub async fn sender_task(
90 mut rx: UnboundedReceiver<TalkProtocol>,
91 mut write: SplitSink<WebStream, Message>,
92 ) {
93 while let Some(msg) = rx.next().await {
94 match bincode::serialize(&msg) {
95 Ok(bin) => {
96 if let Err(e) = write.send(Message::Binary(bin)).await {
97 eprintln!("WebSocket send error: {:?}", e);
98 break;
99 }
100 }
101 Err(e) => {
102 eprintln!("Serialization error: {:?}", e);
103 }
104 }
105 }
106
107 println!("Sender task ended");
108 }
109
110 pub async fn receiver_task(
111 mut read: SplitStream<WebStream>,
112 mut on_message: impl FnMut(TalkProtocol) + Send + 'static,
113 ) -> Result<(), WsError> {
114 while let Some(msg) = read.next().await {
115 match msg {
116 Ok(Message::Binary(bin)) => {
117 if let Ok(parsed) = TalkProtocol::deserialize(&bin) {
118 on_message(parsed);
119 }
120 }
121 Ok(Message::Text(text)) => {
122 println!("Received text message: {}", text);
124 }
125 Ok(_) => {} Err(e) => return Err(e),
127 }
128 }
129 Ok(())
130 }
131}
132
133pub mod wasm {
136 use super::TalkProtocol;
137 use futures_channel::mpsc::UnboundedReceiver;
138 use futures_util::SinkExt;
139 use futures_util::StreamExt;
140 use futures_util::stream::{SplitSink, SplitStream};
142 use gloo_net::websocket::Message;
143 use gloo_net::websocket::futures::WebSocket;
144 use gloo_utils::errors::JsError;
145 use log::Level;
146 use log::info;
147 use yew::prelude::*;
148
149 pub fn connect_websocket(url: &str) -> Result<WebSocket, JsError> {
150 WebSocket::open(url)
151 }
152
153 pub async fn sender_task(
154 mut rx: UnboundedReceiver<TalkProtocol>,
155 mut write: SplitSink<WebSocket, Message>,
156 ) {
157 while let Some(msg) = rx.next().await {
158 match bincode::serialize(&msg) {
159 Ok(bin) => {
160 if let Err(_e) = write.send(Message::Bytes(bin)).await {
161 break;
162 }
163 }
164 Err(e) => panic!("Sending message failed {}", e),
165 }
166 }
167
168 println!("Sender task ended");
169 }
170
171 pub async fn receiver_task(
172 mut read: SplitStream<WebSocket>,
173 messages: UseStateHandle<Vec<TalkProtocol>>,
174 ) {
175 let messages = messages.clone();
176 while let Some(msg) = read.next().await {
177 match msg {
178 Ok(Message::Bytes(bin)) => {
179 if let Ok(parsed) = TalkProtocol::deserialize(&bin) {
180 let mut current = (*messages).clone();
181 current.push(parsed.clone());
182 messages.set(current);
183
184 let _ = console_log::init_with_level(Level::Debug);
185 info!("Received bytes message: {:?}", parsed);
186 }
187 }
188 Ok(Message::Text(text)) => {
189 println!("Received text message: {}", text);
191 }
192 Err(_e) => (),
193 }
194 }
195 }
196}
197
198#[cfg(target_arch = "wasm32")]
199pub use wasm::*; #[cfg(not(target_arch = "wasm32"))]
202pub use native::*;