alkahest 0.3.0

Fantastic serialization library with zero-overhead serialization and zero-copy deserialization
Documentation
use alloc::{string::String, vec::Vec};

use rand::{
    distributions::{Alphanumeric, DistString},
    Rng, SeedableRng,
};

use crate::{
    alkahest, read_packet, write_packet_to_vec, Formula, Lazy, SerIter, Serialize, SerializeRef,
};

#[derive(Debug, Clone, PartialEq, Eq)]
#[alkahest(Formula, Serialize, Deserialize)]
pub enum GameMessage {
    Client(ClientMessage),
    Server(ServerMessage),
}

#[derive(Debug)]
#[alkahest(Deserialize<'de, GameMessage>)]
pub enum GameMessageRead<'de> {
    Client(ClientMessageRead<'de>),
    Server(ServerMessageRead<'de>),
}

#[derive(Debug, PartialEq, Eq, Clone)]
#[alkahest(Formula, Serialize, Deserialize)]
pub enum ClientMessage {
    ClientData { nickname: String, clan: String },
    Chat(String),
}

#[derive(Debug)]
#[alkahest(Deserialize<'de, ClientMessage>)]
pub enum ClientMessageRead<'de> {
    ClientData { nickname: &'de str, clan: &'de str },
    Chat(&'de str),
}

#[derive(Debug, PartialEq, Eq, Clone)]
#[alkahest(Formula, Serialize, Deserialize)]
pub enum ServerMessage {
    ServerData(u64),
    ClientChat { client_id: u64, message: String },
}

#[derive(Debug)]
#[alkahest(Deserialize<'de, ServerMessage>)]
pub enum ServerMessageRead<'de> {
    ServerData(u64),
    ClientChat { client_id: u64, message: &'de str },
}

#[derive(Debug)]
#[alkahest(Formula, Serialize, Deserialize)]
pub struct NetPacket<G> {
    pub game_messages: Vec<G>,
}

#[derive(Debug)]
#[alkahest(for<X: Formula> Serialize<NetPacket<X>> where G: Serialize<[X]>)]
#[alkahest(for<X: Formula> SerializeRef<NetPacket<X>> where G: SerializeRef<[X]>)]
pub struct NetPacketWrite<G> {
    pub game_messages: G,
}

#[derive(Debug)]
#[alkahest(Deserialize<'de, NetPacket::<G>> where G: Formula)]
pub struct NetPacketRead<'de, G> {
    pub game_messages: Lazy<'de, [G]>,
}

fn get_string(rng: &mut impl Rng) -> String {
    Alphanumeric.sample_string(rng, 8)
}

fn messages<'a>(mut rng: impl Rng + 'a, len: usize) -> impl Iterator<Item = GameMessage> + 'a {
    core::iter::repeat_with(move || match rng.gen_range(0..4) {
        0 => GameMessage::Client(ClientMessage::ClientData {
            nickname: get_string(&mut rng),
            clan: get_string(&mut rng),
        }),
        1 => GameMessage::Client(ClientMessage::Chat(get_string(&mut rng))),
        2 => GameMessage::Server(ServerMessage::ClientChat {
            client_id: rng.gen(),
            message: get_string(&mut rng),
        }),
        3 => GameMessage::Server(ServerMessage::ServerData(rng.gen())),
        _ => unreachable!(),
    })
    .take(len)
}

#[cfg(all(feature = "alloc", feature = "derive"))]
#[test]
fn test_net_packet() {
    let rng = rand::rngs::SmallRng::from_rng(rand::thread_rng()).unwrap();

    #[cfg(feature = "fixed8")]
    const LEN: usize = 1;

    #[cfg(not(feature = "fixed8"))]
    const LEN: usize = 1000;

    let mut buffer = Vec::new();
    let size = write_packet_to_vec::<NetPacket<GameMessage>, _>(
        NetPacketWrite {
            game_messages: SerIter(messages(rng.clone(), LEN)),
        },
        &mut buffer,
    );

    let mut buffer2 = Vec::new();
    let size2 = write_packet_to_vec::<NetPacket<GameMessage>, _>(
        NetPacket {
            game_messages: messages(rng, LEN).collect::<Vec<_>>(),
        },
        &mut buffer2,
    );

    assert_eq!(size, size2);
    assert_eq!(buffer[..size], buffer2[..size]);

    let (packet, _) =
        read_packet::<NetPacket<GameMessage>, NetPacketRead<GameMessage>>(&buffer[..]).unwrap();

    for message in packet.game_messages.iter::<GameMessageRead>() {
        match message.unwrap() {
            GameMessageRead::Client(ClientMessageRead::ClientData { nickname, clan }) => {
                drop(nickname);
                drop(clan);
            }
            GameMessageRead::Client(ClientMessageRead::Chat(message)) => {
                drop(message);
            }
            GameMessageRead::Server(ServerMessageRead::ServerData(data)) => {
                drop(data);
            }
            GameMessageRead::Server(ServerMessageRead::ClientChat { client_id, message }) => {
                drop(client_id);
                drop(message);
            }
        }
    }
}