desert_core 0.1.5

Binary serialization library for Rust (core crate)
Documentation
use hashbrown::{HashMap, HashSet};
use lazy_static::lazy_static;

use crate::deserializer::DeserializationContext;
use crate::error::Result;
use crate::serializer::SerializationContext;
use crate::{BinaryDeserializer, BinaryInput, BinaryOutput, BinarySerializer, Evolution};

mod deserializer;
mod serializer;

pub use deserializer::AdtDeserializer;
pub use serializer::AdtSerializer;

lazy_static! {
    pub static ref EMPTY_ADT_METADATA: AdtMetadata =
        AdtMetadata::new(vec![Evolution::InitialVersion]);
}

#[derive(Debug)]
pub struct AdtMetadata {
    version: u8,
    field_generations: HashMap<String, u8>,
    made_optional_at: HashMap<String, u8>,
    removed_fields: HashSet<String>,
    evolution_steps: Vec<Evolution>,
}

impl AdtMetadata {
    pub fn new(evolution_steps: Vec<Evolution>) -> Self {
        if evolution_steps.len() > 255 {
            panic!("Too many evolution steps");
        }

        let field_generations = evolution_steps
            .iter()
            .enumerate()
            .filter_map(|(idx, evolution)| {
                if let Evolution::FieldAdded { name, .. } = evolution {
                    Some((name.clone(), idx as u8))
                } else {
                    None
                }
            })
            .collect();

        let made_optional_at = evolution_steps
            .iter()
            .enumerate()
            .filter_map(|(idx, evolution)| {
                if let Evolution::FieldMadeOptional { name } = evolution {
                    Some((name.clone(), idx as u8))
                } else {
                    None
                }
            })
            .collect();

        let removed_fields = evolution_steps
            .iter()
            .filter_map(|evolution| {
                if let Evolution::FieldRemoved { name } = evolution {
                    Some(name.clone())
                } else {
                    None
                }
            })
            .collect();

        Self {
            version: (evolution_steps.len() - 1) as u8,
            field_generations,
            made_optional_at,
            removed_fields,
            evolution_steps,
        }
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FieldPosition {
    pub chunk: u8,
    pub position: u8,
}

impl FieldPosition {
    const EMPTY: Self = FieldPosition {
        chunk: 0,
        position: 0,
    };

    pub fn new(chunk: u8, position: u8) -> Self {
        Self { chunk, position }
    }

    pub fn to_byte(&self) -> u8 {
        if self.chunk == 0 {
            (-(self.position as i8)) as u8
        } else {
            self.chunk
        }
    }
}

impl BinarySerializer for FieldPosition {
    fn serialize<Output: BinaryOutput>(
        &self,
        context: &mut SerializationContext<Output>,
    ) -> Result<()> {
        context.write_u8(self.to_byte());
        Ok(())
    }
}

impl BinaryDeserializer for FieldPosition {
    fn deserialize(context: &mut DeserializationContext<'_>) -> Result<Self> {
        let byte = context.read_i8()?;
        if byte < 0 {
            Ok(FieldPosition::new(0, (-byte) as u8))
        } else {
            Ok(FieldPosition::new(byte as u8, 0))
        }
    }
}