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);
}
}
}