use crate::client_id::ClientId;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::{io::Cursor, net::IpAddr};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub enum MessageData {
Broadcast { from: ClientId, data: Vec<u8> },
Send { from: ClientId, to: ClientId, data: Vec<u8> },
ClientJoined(ClientId),
ClientLeft(ClientId),
}
pub(crate) enum MessageDataInternal {
Broadcast(ClientId, Vec<u8>),
Send(ClientId, ClientId, Vec<u8>),
ServerUuid(ClientId),
ClientJoined(ClientId),
ClientLeft(ClientId),
PromoteToHost(ClientId, IpAddr, u16),
NewHost(IpAddr, u16),
}
impl From<MessageData> for MessageDataInternal {
fn from(value: MessageData) -> Self {
match value {
MessageData::Broadcast {
from: sender,
data: m,
} => MessageDataInternal::Broadcast(sender, m),
MessageData::Send {
from: sender,
to: dst,
data: m,
} => MessageDataInternal::Send(sender, dst, m),
MessageData::ClientJoined(uuid) => MessageDataInternal::ClientJoined(uuid),
MessageData::ClientLeft(uuid) => MessageDataInternal::ClientLeft(uuid),
}
}
}
const MESSAGE_KIND_BROADCAST: u8 = 0;
const MESSAGE_KIND_CLIENT_JOINED: u8 = 1;
const MESSAGE_KIND_SERVER_UUID: u8 = 2;
const MESSAGE_KIND_SEND: u8 = 3;
const MESSAGE_KIND_CLIENT_LEFT: u8 = 4;
const MESSAGE_KIND_PROMOTE_TO_HOST: u8 = 5;
const MESSAGE_KIND_NEW_HOST: u8 = 6;
impl TryFrom<&[u8]> for MessageDataInternal {
type Error = Box<dyn std::error::Error>;
fn try_from(value: &[u8]) -> Result<Self> {
if value.is_empty() {
return Err("empty message, can't deserialize".into());
}
let t = value[0];
Ok(match t {
MESSAGE_KIND_BROADCAST => {
let mut c = Cursor::new(&value[1..17]);
let sender = ClientId::from_u128(c.read_u128::<BigEndian>()?);
let data = value[17..].to_vec();
MessageDataInternal::Broadcast(sender, data)
}
MESSAGE_KIND_CLIENT_JOINED => {
let mut c = Cursor::new(&value[1..17]);
let uuid = ClientId::from_u128(c.read_u128::<BigEndian>()?);
MessageDataInternal::ClientJoined(uuid)
}
MESSAGE_KIND_SERVER_UUID => {
let mut c = Cursor::new(&value[1..17]);
let uuid = ClientId::from_u128(c.read_u128::<BigEndian>()?);
MessageDataInternal::ServerUuid(uuid)
}
MESSAGE_KIND_SEND => {
let mut c = Cursor::new(&value[1..33]);
let sender = ClientId::from_u128(c.read_u128::<BigEndian>()?);
let dst = ClientId::from_u128(c.read_u128::<BigEndian>()?);
let data = value[33..].to_vec();
MessageDataInternal::Send(sender, dst, data)
}
MESSAGE_KIND_CLIENT_LEFT => {
let mut c = Cursor::new(&value[1..17]);
let uuid = ClientId::from_u128(c.read_u128::<BigEndian>()?);
MessageDataInternal::ClientLeft(uuid)
}
MESSAGE_KIND_PROMOTE_TO_HOST => {
let mut c = Cursor::new(&value[1..19]);
let uuid = ClientId::from_u128(c.read_u128::<BigEndian>()?);
let port = c.read_u16::<BigEndian>()?;
let rest = &value[19..];
let ip: IpAddr = if rest.len() == 16 {
let rest: [u8; 16] = rest.try_into()?;
rest.into()
} else {
let rest: [u8; 4] = rest.try_into()?;
rest.into()
};
MessageDataInternal::PromoteToHost(uuid, ip, port)
}
MESSAGE_KIND_NEW_HOST => {
let mut c = Cursor::new(&value[1..3]);
let port = c.read_u16::<BigEndian>()?;
let rest = &value[3..];
let ip: IpAddr = if rest.len() == 16 {
let rest: [u8; 16] = rest.try_into()?;
rest.into()
} else {
let rest: [u8; 4] = rest.try_into()?;
rest.into()
};
MessageDataInternal::NewHost(ip, port)
}
_ => {
return Err("message type not recognized".into());
}
})
}
}
impl TryFrom<MessageDataInternal> for Vec<u8> {
type Error = Box<dyn std::error::Error>;
fn try_from(value: MessageDataInternal) -> Result<Self> {
let (t, partial) = match value {
MessageDataInternal::Broadcast(sender, m) => {
let mut res: Vec<u8> = vec![0; 16];
let mut c = Cursor::new(&mut res);
c.write_u128::<BigEndian>(sender.as_u128())?;
res.extend_from_slice(&m);
(MESSAGE_KIND_BROADCAST, res)
}
MessageDataInternal::ClientJoined(uuid) => {
let mut res: Vec<u8> = vec![0; 16];
let mut c = Cursor::new(&mut res);
c.write_u128::<BigEndian>(uuid.as_u128())?;
(MESSAGE_KIND_CLIENT_JOINED, res)
}
MessageDataInternal::ServerUuid(uuid) => {
let mut res: Vec<u8> = vec![0; 16];
let mut c = Cursor::new(&mut res);
c.write_u128::<BigEndian>(uuid.as_u128())?;
(MESSAGE_KIND_SERVER_UUID, res)
}
MessageDataInternal::Send(sender, dst, m) => {
let mut res: Vec<u8> = vec![0; 32];
let mut c = Cursor::new(&mut res);
c.write_u128::<BigEndian>(sender.as_u128())?;
c.write_u128::<BigEndian>(dst.as_u128())?;
res.extend_from_slice(&m);
(MESSAGE_KIND_SEND, res)
}
MessageDataInternal::ClientLeft(uuid) => {
let mut res: Vec<u8> = vec![0; 16];
let mut c = Cursor::new(&mut res);
c.write_u128::<BigEndian>(uuid.as_u128())?;
(MESSAGE_KIND_CLIENT_LEFT, res)
}
MessageDataInternal::PromoteToHost(uuid, ip, port) => {
let mut res: Vec<u8> = vec![0; 18];
let mut c = Cursor::new(&mut res);
c.write_u128::<BigEndian>(uuid.as_u128())?;
c.write_u16::<BigEndian>(port)?;
match ip {
IpAddr::V4(v4) => res.extend_from_slice(&v4.octets()),
IpAddr::V6(v6) => res.extend_from_slice(&v6.octets()),
}
(MESSAGE_KIND_PROMOTE_TO_HOST, res)
}
MessageDataInternal::NewHost(ip, port) => {
let mut res: Vec<u8> = vec![0; 2];
let mut c = Cursor::new(&mut res);
c.write_u16::<BigEndian>(port)?;
match ip {
IpAddr::V4(v4) => res.extend_from_slice(&v4.octets()),
IpAddr::V6(v6) => res.extend_from_slice(&v6.octets()),
}
(MESSAGE_KIND_NEW_HOST, res)
}
};
let mut res: Vec<u8> = vec![t];
res.extend_from_slice(&partial);
Ok(res)
}
}
#[cfg(test)]
mod test {
use std::string::FromUtf8Error;
use super::*;
struct Msg(String);
impl From<String> for Msg {
fn from(value: String) -> Self {
Self(value)
}
}
impl TryFrom<&[u8]> for Msg {
type Error = FromUtf8Error;
fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
Ok(Msg(String::from_utf8(value.to_vec())?))
}
}
impl TryFrom<Msg> for Vec<u8> {
type Error = ();
fn try_from(value: Msg) -> std::result::Result<Self, Self::Error> {
Ok(value.0.into())
}
}
#[test]
fn test_in_out() {
in_out(MessageDataInternal::Broadcast(
ClientId::new(),
"a".to_string().into(),
));
in_out(MessageDataInternal::Send(
ClientId::new(),
ClientId::new(),
"a".to_string().into(),
));
in_out(MessageDataInternal::ClientJoined(ClientId::new()));
in_out(MessageDataInternal::ClientLeft(ClientId::new()));
in_out(MessageDataInternal::ServerUuid(ClientId::new()));
in_out(MessageDataInternal::PromoteToHost(
ClientId::new(),
"127.0.0.1".parse().unwrap(),
444,
));
in_out(MessageDataInternal::PromoteToHost(
ClientId::new(),
"::1".parse().unwrap(),
444,
));
in_out(MessageDataInternal::NewHost(
"127.0.0.1".parse().unwrap(),
444,
));
in_out(MessageDataInternal::NewHost("::1".parse().unwrap(), 444));
}
fn in_out(m: MessageDataInternal) {
let v: Vec<u8> = m.try_into().unwrap();
let _m: MessageDataInternal = v.as_slice().try_into().unwrap();
}
}