gaia-binary 0.1.1

Binary encoding and decoding for Gaia project
Documentation
use crate::{traits::BinaryFormat, TrackingReader};
use gaia_types::{GaiaError, Result};
use std::io::{Read, Seek};

/// Generic binary reader with position tracking.
pub struct BinaryReader<R: Read, F: BinaryFormat> {
    inner: TrackingReader<R>,
    diagnostics: Vec<GaiaError>,
    _marker: std::marker::PhantomData<F>,
}

impl<R: Read, F: BinaryFormat> BinaryReader<R, F> {
    /// Create a new binary reader.
    pub fn new(inner: R) -> Self {
        Self { inner: TrackingReader { inner, position: 0 }, diagnostics: Vec::new(), _marker: std::marker::PhantomData }
    }

    /// Get current position.
    pub fn position(&self) -> u64 {
        self.inner.position
    }

    /// Get current position (alias for position).
    pub fn get_position(&self) -> u64 {
        self.inner.position
    }

    /// Set current position (alias for seek).
    pub fn set_position(&mut self, pos: u64) -> Result<u64>
    where
        R: std::io::Seek,
    {
        use std::io::Seek;
        self.seek(std::io::SeekFrom::Start(pos)).map_err(|_| GaiaError::invalid_data("Seek failed"))
    }

    /// Get stream position.
    pub fn stream_position(&mut self) -> Result<u64>
    where
        R: std::io::Seek,
    {
        use std::io::Seek;
        self.inner.stream_position().map_err(|_| GaiaError::invalid_data("Stream position failed"))
    }

    /// Add a diagnostic error.
    pub fn add_error(&mut self, error: GaiaError) {
        self.diagnostics.push(error);
    }

    /// Take all collected diagnostic errors.
    pub fn take_errors(&mut self) -> Vec<GaiaError> {
        std::mem::take(&mut self.diagnostics)
    }

    /// Read a u8 value.
    pub fn read_u8(&mut self) -> Result<u8> {
        let mut buf = [0u8; 1];
        self.inner.read_exact(&mut buf).map_err(|_| GaiaError::truncated())?;
        Ok(buf[0])
    }

    /// Read a u16 value.
    pub fn read_u16(&mut self) -> Result<u16> {
        F::read_u16(&mut self.inner)
    }

    /// Read a u32 value.
    pub fn read_u32(&mut self) -> Result<u32> {
        F::read_u32(&mut self.inner)
    }

    /// Read a u64 value.
    pub fn read_u64(&mut self) -> Result<u64> {
        F::read_u64(&mut self.inner)
    }

    /// Read an i16 value.
    pub fn read_i16(&mut self) -> Result<i16> {
        F::read_i16(&mut self.inner)
    }

    /// Read an i32 value.
    pub fn read_i32(&mut self) -> Result<i32> {
        F::read_i32(&mut self.inner)
    }

    /// Read an i64 value.
    pub fn read_i64(&mut self) -> Result<i64> {
        F::read_i64(&mut self.inner)
    }

    /// Read an f32 value.
    pub fn read_f32(&mut self) -> Result<f32> {
        F::read_f32(&mut self.inner)
    }

    /// Read an f64 value.
    pub fn read_f64(&mut self) -> Result<f64> {
        F::read_f64(&mut self.inner)
    }

    /// Read raw bytes.
    pub fn read_bytes(&mut self, len: usize) -> Result<Vec<u8>> {
        let mut buf = vec![0u8; len];
        self.inner.read_exact(&mut buf).map_err(|_| GaiaError::truncated())?;
        Ok(buf)
    }

    /// Read into a buffer.
    pub fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
        self.inner.read_exact(buf).map_err(|_| GaiaError::truncated())
    }

    /// Read to end of source.
    pub fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
        self.inner.read_to_end(buf).map_err(|_| GaiaError::truncated())
    }
}

impl<R: Read + std::io::Seek, F: BinaryFormat> std::io::Seek for BinaryReader<R, F> {
    fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
        self.inner.seek(pos)
    }
}

impl<R: Read + std::io::Seek, F: BinaryFormat> BinaryReader<R, F> {
    /// Seek to a position.
    pub fn seek_gaia(&mut self, pos: std::io::SeekFrom) -> Result<u64> {
        self.seek(pos).map_err(|_| GaiaError::invalid_data("Seek failed"))
    }
}

impl<R: Read + std::fmt::Debug, F: BinaryFormat> std::fmt::Debug for BinaryReader<R, F> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("BinaryReader").field("inner", &self.inner).field("position", &self.position()).finish()
    }
}