tubes/
data.rs

1use anyhow::{Error, Result, anyhow};
2use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
3use std::{io::Cursor, net::IpAddr};
4use uuid::Uuid;
5
6/// A wrapper for a message being received.
7///
8/// This can be a broadcast ([MessageData::Broadcast]) or a direct message
9/// ([MessageData::Send]).
10///
11/// When a client joins or leaves, [MessageData::ClientJoined] and
12/// [MessageData::ClientLeft] are also received containing the [Uuid] of the
13/// client that joined or left.
14pub enum MessageData {
15    Broadcast { from: Uuid, data: Vec<u8> },
16    Send { from: Uuid, to: Uuid, data: Vec<u8> },
17    ClientJoined(Uuid),
18    ClientLeft(Uuid),
19}
20
21pub(crate) enum MessageDataInternal {
22    Broadcast(Uuid, Vec<u8>),
23    Send(Uuid, Uuid, Vec<u8>),
24    ServerUuid(Uuid),
25    ClientJoined(Uuid),
26    ClientLeft(Uuid),
27    PromoteToHost(Uuid, IpAddr, u16),
28    NewHost(IpAddr, u16),
29}
30
31impl From<MessageData> for MessageDataInternal {
32    fn from(value: MessageData) -> Self {
33        match value {
34            MessageData::Broadcast {
35                from: sender,
36                data: m,
37            } => MessageDataInternal::Broadcast(sender, m),
38            MessageData::Send {
39                from: sender,
40                to: dst,
41                data: m,
42            } => MessageDataInternal::Send(sender, dst, m),
43            MessageData::ClientJoined(uuid) => MessageDataInternal::ClientJoined(uuid),
44            MessageData::ClientLeft(uuid) => MessageDataInternal::ClientLeft(uuid),
45        }
46    }
47}
48
49const MESSAGE_KIND_BROADCAST: u8 = 0;
50const MESSAGE_KIND_CLIENT_JOINED: u8 = 1;
51const MESSAGE_KIND_SERVER_UUID: u8 = 2;
52const MESSAGE_KIND_SEND: u8 = 3;
53const MESSAGE_KIND_CLIENT_LEFT: u8 = 4;
54const MESSAGE_KIND_PROMOTE_TO_HOST: u8 = 5;
55const MESSAGE_KIND_NEW_HOST: u8 = 6;
56
57impl TryFrom<&[u8]> for MessageDataInternal {
58    type Error = Error;
59
60    fn try_from(value: &[u8]) -> Result<Self> {
61        // Because MessageData cannot be serde (M is generic) the to/from
62        // Vec<u8> will have to decode manually.
63        if value.is_empty() {
64            return Err(anyhow!("empty message, can't deserialize"));
65        }
66        let t = value[0];
67        Ok(match t {
68            MESSAGE_KIND_BROADCAST => {
69                let mut c = Cursor::new(&value[1..17]);
70                let sender = Uuid::from_u128(c.read_u128::<BigEndian>()?);
71                let data = value[17..].to_vec();
72                MessageDataInternal::Broadcast(sender, data)
73            }
74            MESSAGE_KIND_CLIENT_JOINED => {
75                let mut c = Cursor::new(&value[1..17]);
76                let uuid = Uuid::from_u128(c.read_u128::<BigEndian>()?);
77                MessageDataInternal::ClientJoined(uuid)
78            }
79            MESSAGE_KIND_SERVER_UUID => {
80                let mut c = Cursor::new(&value[1..17]);
81                let uuid = Uuid::from_u128(c.read_u128::<BigEndian>()?);
82                MessageDataInternal::ServerUuid(uuid)
83            }
84            MESSAGE_KIND_SEND => {
85                let mut c = Cursor::new(&value[1..33]);
86                let sender = Uuid::from_u128(c.read_u128::<BigEndian>()?);
87                let dst = Uuid::from_u128(c.read_u128::<BigEndian>()?);
88                let data = value[33..].to_vec();
89                MessageDataInternal::Send(sender, dst, data)
90            }
91            MESSAGE_KIND_CLIENT_LEFT => {
92                let mut c = Cursor::new(&value[1..17]);
93                let uuid = Uuid::from_u128(c.read_u128::<BigEndian>()?);
94                MessageDataInternal::ClientLeft(uuid)
95            }
96            MESSAGE_KIND_PROMOTE_TO_HOST => {
97                let mut c = Cursor::new(&value[1..19]);
98                let uuid = Uuid::from_u128(c.read_u128::<BigEndian>()?);
99                let port = c.read_u16::<BigEndian>()?;
100                let rest = &value[19..];
101                let ip: IpAddr = if rest.len() == 16 {
102                    let rest: [u8; 16] = rest.try_into()?;
103                    rest.into()
104                } else {
105                    let rest: [u8; 4] = rest.try_into()?;
106                    rest.into()
107                };
108                MessageDataInternal::PromoteToHost(uuid, ip, port)
109            }
110            MESSAGE_KIND_NEW_HOST => {
111                let mut c = Cursor::new(&value[1..3]);
112                let port = c.read_u16::<BigEndian>()?;
113                let rest = &value[3..];
114                let ip: IpAddr = if rest.len() == 16 {
115                    let rest: [u8; 16] = rest.try_into()?;
116                    rest.into()
117                } else {
118                    let rest: [u8; 4] = rest.try_into()?;
119                    rest.into()
120                };
121                MessageDataInternal::NewHost(ip, port)
122            }
123            _ => {
124                return Err(anyhow!("message type not recognized"));
125            }
126        })
127    }
128}
129
130impl TryFrom<MessageDataInternal> for Vec<u8> {
131    type Error = Error;
132
133    fn try_from(value: MessageDataInternal) -> Result<Self> {
134        // Because MessageData cannot be serde (M is generic) the to/from
135        // Vec<u8> will have to encode manually.
136        let (t, partial) = match value {
137            MessageDataInternal::Broadcast(sender, m) => {
138                let mut res: Vec<u8> = vec![0; 16];
139                let mut c = Cursor::new(&mut res);
140                c.write_u128::<BigEndian>(sender.as_u128())?;
141                res.extend_from_slice(&m);
142                (MESSAGE_KIND_BROADCAST, res)
143            }
144            MessageDataInternal::ClientJoined(uuid) => {
145                let mut res: Vec<u8> = vec![0; 16];
146                let mut c = Cursor::new(&mut res);
147                c.write_u128::<BigEndian>(uuid.as_u128())?;
148                (MESSAGE_KIND_CLIENT_JOINED, res)
149            }
150            MessageDataInternal::ServerUuid(uuid) => {
151                let mut res: Vec<u8> = vec![0; 16];
152                let mut c = Cursor::new(&mut res);
153                c.write_u128::<BigEndian>(uuid.as_u128())?;
154                (MESSAGE_KIND_SERVER_UUID, res)
155            }
156            MessageDataInternal::Send(sender, dst, m) => {
157                let mut res: Vec<u8> = vec![0; 32];
158                let mut c = Cursor::new(&mut res);
159                c.write_u128::<BigEndian>(sender.as_u128())?;
160                c.write_u128::<BigEndian>(dst.as_u128())?;
161                res.extend_from_slice(&m);
162                (MESSAGE_KIND_SEND, res)
163            }
164            MessageDataInternal::ClientLeft(uuid) => {
165                let mut res: Vec<u8> = vec![0; 16];
166                let mut c = Cursor::new(&mut res);
167                c.write_u128::<BigEndian>(uuid.as_u128())?;
168                (MESSAGE_KIND_CLIENT_LEFT, res)
169            }
170            MessageDataInternal::PromoteToHost(uuid, ip, port) => {
171                let mut res: Vec<u8> = vec![0; 18];
172                let mut c = Cursor::new(&mut res);
173                c.write_u128::<BigEndian>(uuid.as_u128())?;
174                c.write_u16::<BigEndian>(port)?;
175                match ip {
176                    IpAddr::V4(v4) => res.extend_from_slice(&v4.octets()),
177                    IpAddr::V6(v6) => res.extend_from_slice(&v6.octets()),
178                }
179                (MESSAGE_KIND_PROMOTE_TO_HOST, res)
180            }
181            MessageDataInternal::NewHost(ip, port) => {
182                let mut res: Vec<u8> = vec![0; 2];
183                let mut c = Cursor::new(&mut res);
184                c.write_u16::<BigEndian>(port)?;
185                match ip {
186                    IpAddr::V4(v4) => res.extend_from_slice(&v4.octets()),
187                    IpAddr::V6(v6) => res.extend_from_slice(&v6.octets()),
188                }
189                (MESSAGE_KIND_NEW_HOST, res)
190            }
191        };
192        let mut res: Vec<u8> = vec![t];
193        res.extend_from_slice(&partial);
194        Ok(res)
195    }
196}
197
198#[cfg(test)]
199mod test {
200    use std::string::FromUtf8Error;
201
202    use super::*;
203
204    struct Msg(String);
205
206    impl From<String> for Msg {
207        fn from(value: String) -> Self {
208            Self(value)
209        }
210    }
211
212    impl TryFrom<&[u8]> for Msg {
213        type Error = FromUtf8Error;
214
215        fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
216            Ok(Msg(String::from_utf8(value.to_vec())?))
217        }
218    }
219
220    impl TryFrom<Msg> for Vec<u8> {
221        type Error = ();
222
223        fn try_from(value: Msg) -> std::result::Result<Self, Self::Error> {
224            Ok(value.0.into())
225        }
226    }
227
228    #[test]
229    fn test_in_out() {
230        in_out(MessageDataInternal::Broadcast(
231            Uuid::new_v4(),
232            "a".to_string().into(),
233        ));
234        in_out(MessageDataInternal::Send(
235            Uuid::new_v4(),
236            Uuid::new_v4(),
237            "a".to_string().into(),
238        ));
239        in_out(MessageDataInternal::ClientJoined(Uuid::new_v4()));
240        in_out(MessageDataInternal::ClientLeft(Uuid::new_v4()));
241        in_out(MessageDataInternal::ServerUuid(Uuid::new_v4()));
242        in_out(MessageDataInternal::PromoteToHost(
243            Uuid::new_v4(),
244            "127.0.0.1".parse().unwrap(),
245            444,
246        ));
247        in_out(MessageDataInternal::PromoteToHost(
248            Uuid::new_v4(),
249            "::1".parse().unwrap(),
250            444,
251        ));
252        in_out(MessageDataInternal::NewHost(
253            "127.0.0.1".parse().unwrap(),
254            444,
255        ));
256        in_out(MessageDataInternal::NewHost("::1".parse().unwrap(), 444));
257    }
258
259    fn in_out(m: MessageDataInternal) {
260        let v: Vec<u8> = m.try_into().unwrap();
261        let _m: MessageDataInternal = v.as_slice().try_into().unwrap();
262    }
263}