hts 0.1.17

Rust binding for htslib
Documentation
use crate::hts_ffi::{
    bcf_hdr_destroy, bcf_hdr_read, bcf_read, hts_close, hts_open, htslib_obj, HtslibReturnValue,
};
use crate::{alloc::MemoryPool, HtsError, HtsResult};
use std::{ffi::CString, ops::DerefMut, path::Path, ptr::NonNull};

use super::{VcfReader, VcfRecord};

pub struct VcfFile {
    file: *mut htslib_obj::HtsFile,
    header: NonNull<htslib_obj::BcfHeader>,
    pool: MemoryPool<htslib_obj::Bcf>,
}

impl VcfReader for VcfFile {
    fn read_next(&self) -> HtsResult<Option<VcfRecord>> {
        if let Some(mut core_obj) = self.pool.alloc_obj() {
            let rc = unsafe { bcf_read(self.file, self.header.as_ref(), core_obj.deref_mut()) };
            if rc == -1 {
                return Ok(None);
            }
            rc.into_hts_result()?;
            return Ok(Some(VcfRecord::from_inner(core_obj, self)?));
        }
        Err(HtsError::HtsLibError)
    }
}

impl VcfFile {
    pub fn open<P: AsRef<Path>>(path: P) -> HtsResult<Self> {
        let path = CString::new({
            if let Some(path) = path.as_ref().to_str() {
                path
            } else {
                return Err(HtsError::InvalidFileName);
            }
        })
        .map_err(|_| HtsError::InvalidFileName)?;
        let file =
            unsafe { hts_open(path.as_ptr(), b"rb\0".as_ptr() as *const _).into_hts_result()? };
        let header = unsafe { NonNull::new_unchecked(bcf_hdr_read(file).into_hts_result()?) };
        let pool = MemoryPool::with_capacity(128);
        Ok(Self { file, header, pool })
    }

    pub(crate) fn header(&self) -> &htslib_obj::BcfHeader {
        unsafe { self.header.as_ref() }
    }
}

impl Drop for VcfFile {
    fn drop(&mut self) {
        unsafe {
            bcf_hdr_destroy(self.header.as_mut());
            hts_close(self.file);
        }
    }
}