Skip to main content

tuitalk_shared/
lib.rs

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    // Client -> Server Commands
16    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    // Server -> Client Events
24    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    // Server <-> Client
32    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                    // Optional: Handle text messages if you expect them
123                    println!("Received text message: {}", text);
124                }
125                Ok(_) => {} // Ignore other message types
126                Err(e) => return Err(e),
127            }
128        }
129        Ok(())
130    }
131}
132
133//----------------------------------------------------------------------------------------------------WASM----------------------------------------------------------------------------------------------------
134
135pub 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::lock::Mutex;
141    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                    // Optional: Handle text messages if you expect them
190                    println!("Received text message: {}", text);
191                }
192                Err(_e) => (),
193            }
194        }
195    }
196}
197
198#[cfg(target_arch = "wasm32")]
199pub use wasm::*; // Expose WASM API
200
201#[cfg(not(target_arch = "wasm32"))]
202pub use native::*; // Expose native API