bitfield-serialize 0.1.0

A Rust library for defining and serializing bitfield structures with macro support
Documentation
//! Type definitions for bitfield structures and serialization.

use std::collections::HashMap;

/// Represents a field in a bitfield structure.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BitField {
    /// The name of the field.
    pub name: String,
    /// The number of bits for this field.
    pub bits: usize,
    /// The type of the field.
    pub field_type: FieldType,
}

impl BitField {
    /// Create a new BitField.
    pub fn new(name: impl Into<String>, bits: usize, field_type: FieldType) -> Self {
        Self {
            name: name.into(),
            bits,
            field_type,
        }
    }
}

/// The type of a bitfield field.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FieldType {
    /// 8-bit unsigned integer
    U8,
    /// 16-bit unsigned integer
    U16,
    /// 32-bit unsigned integer
    U32,
    /// 64-bit unsigned integer
    U64,
    /// Nested bitfield structure
    Nested(String),
}

impl FieldType {
    /// Get the default bit size for primitive types.
    pub fn default_bit_size(&self) -> Option<usize> {
        match self {
            FieldType::U8 => Some(8),
            FieldType::U16 => Some(16),
            FieldType::U32 => Some(32),
            FieldType::U64 => Some(64),
            FieldType::Nested(_) => None, // Size depends on the nested structure
        }
    }
}

/// Represents a complete bitfield structure definition.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BitfieldStruct {
    /// The name of the structure.
    pub name: String,
    /// The fields in the structure.
    pub fields: Vec<BitField>,
}

impl BitfieldStruct {
    /// Create a new BitfieldStruct.
    pub fn new(name: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            fields: Vec::new(),
        }
    }

    /// Add a field to the structure.
    pub fn add_field(&mut self, name: impl Into<String>, bits: usize, field_type: FieldType) {
        self.fields.push(BitField::new(name, bits, field_type));
    }

    /// Calculate the total bit size of this structure.
    pub fn bit_size(&self, structs: &HashMap<String, BitfieldStruct>) -> usize {
        self.fields
            .iter()
            .map(|field| match &field.field_type {
                FieldType::Nested(struct_name) => {
                    if let Some(nested) = structs.get(struct_name) {
                        nested.bit_size(structs)
                    } else {
                        0 // Unknown nested structure
                    }
                }
                _ => field.bits,
            })
            .sum()
    }

    /// Calculate the total byte size of this structure (rounded up).
    pub fn byte_size(&self, structs: &HashMap<String, BitfieldStruct>) -> usize {
        self.bit_size(structs).div_ceil(8)
    }

    /// Get a field by name.
    pub fn get_field(&self, name: &str) -> Option<&BitField> {
        self.fields.iter().find(|field| field.name == name)
    }

    /// Get the bit offset of a field within the structure.
    pub fn field_bit_offset(
        &self,
        field_name: &str,
        structs: &HashMap<String, BitfieldStruct>,
    ) -> Option<usize> {
        let mut offset = 0;
        for field in &self.fields {
            if field.name == field_name {
                return Some(offset);
            }
            offset += match &field.field_type {
                FieldType::Nested(struct_name) => {
                    if let Some(nested) = structs.get(struct_name) {
                        nested.bit_size(structs)
                    } else {
                        0
                    }
                }
                _ => field.bits,
            };
        }
        None
    }
}

/// Represents a value stored in a bitfield structure.
#[derive(Debug, Clone, PartialEq)]
pub struct BitfieldValue {
    /// The name of the structure this value represents.
    pub struct_name: String,
    /// The field values.
    pub values: HashMap<String, FieldValue>,
}

impl BitfieldValue {
    /// Create a new BitfieldValue.
    pub fn new(struct_name: impl Into<String>) -> Self {
        Self {
            struct_name: struct_name.into(),
            values: HashMap::new(),
        }
    }

    /// Set a u8 field value.
    pub fn set_u8(&mut self, field_name: impl Into<String>, value: u8) {
        self.values.insert(field_name.into(), FieldValue::U8(value));
    }

    /// Set a u16 field value.
    pub fn set_u16(&mut self, field_name: impl Into<String>, value: u16) {
        self.values
            .insert(field_name.into(), FieldValue::U16(value));
    }

    /// Set a u32 field value.
    pub fn set_u32(&mut self, field_name: impl Into<String>, value: u32) {
        self.values
            .insert(field_name.into(), FieldValue::U32(value));
    }

    /// Set a u64 field value.
    pub fn set_u64(&mut self, field_name: impl Into<String>, value: u64) {
        self.values
            .insert(field_name.into(), FieldValue::U64(value));
    }

    /// Set a nested structure field value.
    pub fn set_nested(&mut self, field_name: impl Into<String>, value: BitfieldValue) {
        self.values
            .insert(field_name.into(), FieldValue::Nested(Box::new(value)));
    }

    /// Get a u8 field value.
    pub fn get_u8(&self, field_name: &str) -> Option<u8> {
        match self.values.get(field_name)? {
            FieldValue::U8(val) => Some(*val),
            _ => None,
        }
    }

    /// Get a u16 field value.
    pub fn get_u16(&self, field_name: &str) -> Option<u16> {
        match self.values.get(field_name)? {
            FieldValue::U16(val) => Some(*val),
            _ => None,
        }
    }

    /// Get a u32 field value.
    pub fn get_u32(&self, field_name: &str) -> Option<u32> {
        match self.values.get(field_name)? {
            FieldValue::U32(val) => Some(*val),
            _ => None,
        }
    }

    /// Get a u64 field value.
    pub fn get_u64(&self, field_name: &str) -> Option<u64> {
        match self.values.get(field_name)? {
            FieldValue::U64(val) => Some(*val),
            _ => None,
        }
    }

    /// Get a nested structure field value.
    pub fn get_nested(&self, field_name: &str) -> Option<&BitfieldValue> {
        match self.values.get(field_name)? {
            FieldValue::Nested(val) => Some(val),
            _ => None,
        }
    }

    /// Get a mutable reference to a nested structure field value.
    pub fn get_nested_mut(&mut self, field_name: &str) -> Option<&mut BitfieldValue> {
        match self.values.get_mut(field_name)? {
            FieldValue::Nested(val) => Some(val),
            _ => None,
        }
    }
}

/// Represents a value stored in a bitfield field.
#[derive(Debug, Clone, PartialEq)]
pub enum FieldValue {
    /// 8-bit unsigned integer value
    U8(u8),
    /// 16-bit unsigned integer value
    U16(u16),
    /// 32-bit unsigned integer value
    U32(u32),
    /// 64-bit unsigned integer value
    U64(u64),
    /// Nested bitfield structure value
    Nested(Box<BitfieldValue>),
}

impl FieldValue {
    /// Convert the field value to a u64 for bit operations.
    pub fn to_u64(&self) -> u64 {
        match self {
            FieldValue::U8(val) => *val as u64,
            FieldValue::U16(val) => *val as u64,
            FieldValue::U32(val) => *val as u64,
            FieldValue::U64(val) => *val,
            FieldValue::Nested(_) => 0, // Nested structures cannot be converted to u64
        }
    }

    /// Get the type of this field value.
    pub fn field_type(&self) -> FieldType {
        match self {
            FieldValue::U8(_) => FieldType::U8,
            FieldValue::U16(_) => FieldType::U16,
            FieldValue::U32(_) => FieldType::U32,
            FieldValue::U64(_) => FieldType::U64,
            FieldValue::Nested(val) => FieldType::Nested(val.struct_name.clone()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_bitfield_struct_creation() {
        let mut bf_struct = BitfieldStruct::new("TestStruct");
        bf_struct.add_field("field1", 4, FieldType::U8);
        bf_struct.add_field("field2", 8, FieldType::U16);

        assert_eq!(bf_struct.name, "TestStruct");
        assert_eq!(bf_struct.fields.len(), 2);
        assert_eq!(bf_struct.fields[0].name, "field1");
        assert_eq!(bf_struct.fields[0].bits, 4);
    }

    #[test]
    fn test_bitfield_value_operations() {
        let mut value = BitfieldValue::new("TestStruct");
        value.set_u8("field1", 42);
        value.set_u16("field2", 1024);

        assert_eq!(value.get_u8("field1"), Some(42));
        assert_eq!(value.get_u16("field2"), Some(1024));
        assert_eq!(value.get_u8("nonexistent"), None);
    }

    #[test]
    fn test_nested_structures() {
        let mut nested = BitfieldValue::new("NestedStruct");
        nested.set_u8("inner_field", 100);

        let mut parent = BitfieldValue::new("ParentStruct");
        parent.set_nested("nested", nested);

        let retrieved = parent.get_nested("nested").unwrap();
        assert_eq!(retrieved.get_u8("inner_field"), Some(100));
    }

    #[test]
    fn test_bit_size_calculation() {
        let mut structs = HashMap::new();

        let mut bf_struct = BitfieldStruct::new("TestStruct");
        bf_struct.add_field("field1", 4, FieldType::U8);
        bf_struct.add_field("field2", 12, FieldType::U16);

        structs.insert("TestStruct".to_string(), bf_struct.clone());

        assert_eq!(bf_struct.bit_size(&structs), 16);
        assert_eq!(bf_struct.byte_size(&structs), 2);
    }

    #[test]
    fn test_field_offset_calculation() {
        let mut structs = HashMap::new();

        let mut bf_struct = BitfieldStruct::new("TestStruct");
        bf_struct.add_field("field1", 4, FieldType::U8);
        bf_struct.add_field("field2", 8, FieldType::U16);
        bf_struct.add_field("field3", 4, FieldType::U8);

        structs.insert("TestStruct".to_string(), bf_struct.clone());

        assert_eq!(bf_struct.field_bit_offset("field1", &structs), Some(0));
        assert_eq!(bf_struct.field_bit_offset("field2", &structs), Some(4));
        assert_eq!(bf_struct.field_bit_offset("field3", &structs), Some(12));
        assert_eq!(bf_struct.field_bit_offset("nonexistent", &structs), None);
    }
}