entab 0.3.3

Record-format file reader
Documentation
use alloc::format;
use core::any::type_name;

use crate::EtError;

/// Readers for formats generated by Agilent instruments
pub mod agilent;
/// Common low-level readers (ints, slices, etc)
pub mod common;
/// Reader for FASTA bioinformatics format
pub mod fasta;
/// Reader for FASTQ bioinformatics format
pub mod fastq;
/// Reader for flow data
pub mod flow;
/// Reader for Inficon Hapsite MS formats
pub mod inficon;
/// Reader for PNG image format
#[cfg(feature = "std")]
pub mod png;
/// Reader for BAM/SAM bioinformatics formats
pub mod sam;
/// Readers for Thermo formats
pub mod thermo;
/// Readers for tab-seperated text format
pub mod tsv;
/// Helpers for TSV parsing
pub mod tsv_inference;
// /// Reader for generic XML
// pub mod xml;

/// The default implementation is `impl FromSlice for ()` to simplify implementations for
/// e.g. state or other objects that don't read from the buffer.
pub trait FromSlice<'b: 's, 's>: Sized + Default {
    /// State is used to track information outside of the current slice scope that's used to create
    /// the value returned.
    type State: core::fmt::Debug + Default + 's;

    /// Given a slice and state, determine how much of the slice needs to be parsed to return a
    /// value and update `consumed` with that amount. If no value can be parsed, return Ok(false),
    /// otherwise return Ok(true) if a value can be parsed.
    ///
    /// # Errors
    /// If the parser fails or if there's not enough data in the buffer, an `EtError` will be returned.
    fn parse(
        _buffer: &[u8],
        _eof: bool,
        _consumed: &mut usize,
        _state: &mut Self::State,
    ) -> Result<bool, EtError> {
        Ok(true)
    }

    /// Given a slice and state, update Self by reading the information about the current record
    /// out.
    ///
    /// # Errors
    /// If buffer can not be interpreted into `Self`, return `EtError`.
    fn get(&mut self, _buffer: &'b [u8], _state: &'s Self::State) -> Result<(), EtError> {
        Ok(())
    }

    /// Essentially the same as `extract` below, but doesn't update the state or consume any space.
    ///
    /// Use only for simple types with defined sizes like u8, i32, &[u8], etc. Using this with more
    /// complex types that rely upon updating `state` in between reads will cause bad and confusing
    /// things to happen!
    ///
    /// # Errors
    /// If parsing fails, an error will be returned.
    fn extract(buffer: &'b [u8], state: &'s Self::State) -> Result<Self, EtError>
    where
        Self::State: 'static,
        Self: 's,
    {
        let mut val = Self::default();
        Self::get(&mut val, buffer, state)?;
        Ok(val)
    }
}

/// Pull a `T` out of the slice, updating state appropriately and incrementing `consumed` to
/// account for bytes used.
///
/// # Errors
/// If an error extracting a value occured or if slice needs to be extended, return `EtError`.
#[inline]
pub(crate) fn extract<'b: 's, 's, T>(
    buffer: &'b [u8],
    consumed: &mut usize,
    state: &'s mut <T as FromSlice<'b, 's>>::State,
) -> Result<T, EtError>
where
    T: FromSlice<'b, 's> + Default,
{
    match extract_opt(buffer, false, consumed, state)? {
        None => Err(format!(
            "Tried to extract {}, but parser indicated no more.",
            type_name::<T>()
        )
        .into()),
        Some(value) => Ok(value),
    }
}

/// Pull a `T` out of the slice, updating state appropriately and incrementing `consumed` to
/// account for bytes used.
///
/// # Errors
/// If an error extracting a value occured or if slice needs to be extended, return `EtError`.
#[inline]
pub(crate) fn extract_opt<'b: 's, 's, T>(
    buffer: &'b [u8],
    eof: bool,
    consumed: &mut usize,
    state: &'s mut <T as FromSlice<'b, 's>>::State,
) -> Result<Option<T>, EtError>
where
    T: FromSlice<'b, 's> + Default,
{
    let start = *consumed;
    if !T::parse(&buffer[start..], eof, consumed, state)? {
        return Ok(None);
    }
    let mut record = T::default();
    T::get(&mut record, &buffer[start..*consumed], state)?;
    Ok(Some(record))
}

/// The endianness of a number used to extract such a number.
#[derive(Clone, Copy, Debug, Default)]
pub enum Endian {
    /// A number stored in big-endian format
    Big,
    /// A number stored in little-endian format
    #[default]
    Little,
}