slow5lib 0.1.0

Rust re-implementation of slow5lib: read and write SLOW5/BLOW5 nanopore sequencing files
Documentation
use std::collections::HashMap;

/// File-level header parsed from a SLOW5/BLOW5 file.
///
/// # Examples
///
/// ```
/// use slow5lib::header::{Header, RecordCompression, SignalCompression, ReadGroup};
/// use slow5lib::aux::AuxMeta;
///
/// let header = Header {
///     version: (0, 2, 0),
///     num_read_groups: 1,
///     record_compression: RecordCompression::Zstd,
///     signal_compression: SignalCompression::SvbZd,
///     read_groups: vec![ReadGroup::default()],
///     aux_meta: AuxMeta::default(),
/// };
/// assert_eq!(header.version, (0, 2, 0));
/// assert_eq!(header.num_read_groups, 1);
/// ```
#[derive(Debug, Clone)]
pub struct Header {
    /// File format version as (major, minor, patch).
    pub version: (u8, u8, u8),
    /// Number of read groups declared in the header.
    pub num_read_groups: u32,
    /// Compression applied to each whole record (BLOW5 only).
    pub record_compression: RecordCompression,
    /// Compression applied to the raw signal within each record (BLOW5 only).
    pub signal_compression: SignalCompression,
    /// Per-read-group data attributes. Index corresponds to read group id.
    pub read_groups: Vec<ReadGroup>,
    /// Schema for auxiliary fields (ordered name + type pairs).
    /// Empty when the file has no auxiliary fields.
    pub aux_meta: crate::aux::AuxMeta,
}

/// Per-read-group key/value attributes from the header (run_id, experiment_name, etc.).
///
/// # Examples
///
/// ```
/// use slow5lib::header::ReadGroup;
///
/// let mut rg = ReadGroup::default();
/// rg.attributes.insert("run_id".to_string(), "abc123".to_string());
/// assert_eq!(rg.attributes["run_id"], "abc123");
/// ```
#[derive(Debug, Clone, Default)]
pub struct ReadGroup {
    /// Attribute values keyed by name. Missing values are stored as `"."`.
    pub attributes: HashMap<String, String>,
}

/// Compression method applied to the binary record buffer.
///
/// Encoded as a single byte in the BLOW5 binary header prefix:
/// 0 = None, 1 = Zlib, 2 = Zstd.
///
/// # Examples
///
/// ```
/// use slow5lib::header::RecordCompression;
///
/// let comp = RecordCompression::Zstd;
/// match comp {
///     RecordCompression::None => println!("uncompressed"),
///     RecordCompression::Zlib => println!("zlib"),
///     RecordCompression::Zstd => println!("zstd"),
/// }
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum RecordCompression {
    #[default]
    None,
    Zlib,
    Zstd,
}

/// Compression method applied to the raw signal within a record.
///
/// Encoded as a single byte in the BLOW5 binary header prefix (version >= 0.2.0):
/// 0 = None, 1 = SvbZd, 2 = ExZd.
///
/// SvbZd is U32Classic StreamVByte with a 4-byte element-count prefix applied to
/// zigzag-delta encoded i16 samples. Wire-compatible with the Lemire C streamvbyte
/// library (not the ONT VBZ/SVB16 format).
///
/// ExZd improves on SvbZd with a quantize-trailing-shift pre-pass and a PFOR-style
/// patched/exception scheme over the zigzag-delta values. Wire-compatible with the
/// C library's `SLOW5_COMPRESS_EX_ZD`.
///
/// # Examples
///
/// ```
/// use slow5lib::header::SignalCompression;
///
/// let comp = SignalCompression::SvbZd;
/// match comp {
///     SignalCompression::None => println!("uncompressed"),
///     SignalCompression::SvbZd => println!("SVB-ZD"),
///     SignalCompression::ExZd => println!("ex-zd"),
/// }
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SignalCompression {
    #[default]
    None,
    SvbZd,
    ExZd,
}

impl RecordCompression {
    pub(crate) fn from_byte(b: u8) -> Option<Self> {
        match b {
            0 => Some(Self::None),
            1 => Some(Self::Zlib),
            2 => Some(Self::Zstd),
            _ => None,
        }
    }

    pub(crate) fn to_byte(self) -> u8 {
        match self {
            Self::None => 0,
            Self::Zlib => 1,
            Self::Zstd => 2,
        }
    }
}

impl SignalCompression {
    pub(crate) fn from_byte(b: u8) -> Option<Self> {
        match b {
            0 => Some(Self::None),
            1 => Some(Self::SvbZd),
            2 => Some(Self::ExZd),
            _ => None,
        }
    }

    pub(crate) fn to_byte(self) -> u8 {
        match self {
            Self::None => 0,
            Self::SvbZd => 1,
            Self::ExZd => 2,
        }
    }
}