mod chromatogram;
mod frame;
mod spectrum;
mod util;
pub use spectrum::{
MZFileReader, MemorySpectrumSource, RandomAccessSpectrumGroupingIterator,
RandomAccessSpectrumIterator, RandomAccessSpectrumSource, SpectrumAccessError,
SpectrumIterator, SpectrumReceiver, SpectrumSource,
SpectrumSourceWithMetadata, SpectrumWriter, StreamingSpectrumIterator,
};
#[cfg(any(feature = "mgf", feature = "mzml"))]
pub(crate) use util::DetailLevelGuard;
pub use util::SeekRead;
pub use frame::{
BorrowedGeneric3DIonMobilityFrameSource, Generic3DIonMobilityFrameSource,
IonMobilityFrameAccessError, IonMobilityFrameIterator,
IonMobilityFrameSource, IonMobilityFrameWriter, RandomAccessIonMobilityFrameIterator,
RandomAccessIonMobilityFrameGroupingIterator,
IntoIonMobilityFrameSourceError,
IntoIonMobilityFrameSource
};
pub use chromatogram::{ChromatogramIterator, ChromatogramSource};
pub use crate::spectrum::group::{SpectrumGrouping, IonMobilityFrameGrouping};
#[cfg(feature = "async_partial")]
pub use spectrum::{AsyncSpectrumSource, AsyncRandomAccessSpectrumIterator, SpectrumStream};
#[cfg(feature = "async_partial")]
pub use chromatogram::AsyncChromatogramSource;
#[cfg(feature = "async_partial")]
pub use frame::{
AsyncGeneric3DIonMobilityFrameSource, AsyncIntoIonMobilityFrameSource,
AsyncIonMobilityFrameSource, AsyncRandomAccessIonMobilityFrameIterator, IonMobilityFrameStream,
};
#[cfg(feature = "mzsignal")]
pub use spectrum::PeakPicking;
#[cfg(feature = "async")]
pub use spectrum::AsyncMZFileReader;
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "mzml")]
#[test]
fn test_failed_detail_checks() -> std::io::Result<()> {
use crate::io::{DetailLevel, OffsetIndex};
for level in [
DetailLevel::Full,
DetailLevel::Lazy,
DetailLevel::MetadataOnly,
] {
let mut reader = crate::MzMLReader::open_path("test/data/small.mzML")?;
reader.set_detail_level(level);
let mut index = OffsetIndex::new("spectrum".into());
index.insert("invalid offset", u64::MAX);
index.init = true;
reader.set_index(index);
assert!(reader.get_spectrum_by_time(1.0).is_none());
assert_eq!(*reader.detail_level(), level);
assert!(reader.has_ion_mobility().is_none());
assert_eq!(*reader.detail_level(), level);
reader.set_index(OffsetIndex::new("spectrum".into()));
assert!(reader.get_spectrum_by_time(1.0).is_none());
assert_eq!(
reader.has_ion_mobility(),
Some(crate::spectrum::HasIonMobility::None)
);
assert_eq!(*reader.detail_level(), level);
}
Ok(())
}
#[test]
fn test_empty_spectrum_time_seek() {
let mut source: MemorySpectrumSource = MemorySpectrumSource::default();
let mut iter = source.iter();
for time in [0.0, -1.0, f64::INFINITY, f64::NAN] {
assert!(matches!(
iter.start_from_time(time),
Err(SpectrumAccessError::SpectrumNotFound)
));
assert!(iter.next().is_none());
assert!(iter.next_back().is_none());
}
let mut scan = crate::spectrum::MultiLayerSpectrum::default();
scan.description.id = "first".into();
let mut source: MemorySpectrumSource = std::collections::VecDeque::from([scan]).into();
let mut iter = source.iter();
assert_eq!(iter.next().unwrap().description.id, "first");
assert!(matches!(
iter.start_from_time(f64::NAN),
Err(SpectrumAccessError::IOError(None))
));
assert!(iter.next().is_none());
iter.start_from_time(0.0).unwrap();
assert_eq!(iter.next().unwrap().description.id, "first");
}
#[test]
fn test_empty_frame_time_seek() {
let source: MemorySpectrumSource = MemorySpectrumSource::default();
let mut source: Generic3DIonMobilityFrameSource<_, _, _> =
Generic3DIonMobilityFrameSource::new(source);
let mut iter = source.iter();
for time in [0.0, -1.0, f64::INFINITY, f64::NAN] {
assert!(matches!(
iter.start_from_time(time),
Err(IonMobilityFrameAccessError::FrameNotFound)
));
assert!(iter.next().is_none());
assert!(iter.next_back().is_none());
}
let scan = crate::spectrum::MultiLayerSpectrum::default();
let source: MemorySpectrumSource = std::collections::VecDeque::from([scan]).into();
let mut source: Generic3DIonMobilityFrameSource<_, _, _> =
Generic3DIonMobilityFrameSource::new(source);
let mut iter = source.iter();
assert!(matches!(
iter.start_from_time(0.0),
Err(IonMobilityFrameAccessError::IOError(None))
));
assert!(iter.next().is_none());
}
#[test]
fn test_object_safe() {
let _f = |_x: &dyn SpectrumSource| {};
}
fn spectra(n: usize) -> MemorySpectrumSource {
use crate::spectrum::{
bindata::{ArrayType, BinaryArrayMap, BinaryDataArrayType, DataArray},
MultiLayerSpectrum,
};
MemorySpectrumSource::new(
(0..n)
.map(|index| {
let mut spectrum = MultiLayerSpectrum::default();
spectrum.description.index = index;
spectrum.description.id = index.to_string();
let mut arrays = BinaryArrayMap::new();
arrays.add(DataArray::wrap(
&ArrayType::MeanInverseReducedIonMobilityArray,
BinaryDataArrayType::Float64,
Vec::new(),
));
spectrum.arrays = Some(arrays);
spectrum
})
.collect(),
)
}
fn check_remaining(
mut it: impl ExactSizeIterator<Item = usize> + DoubleEndedIterator,
n: usize,
) {
let mut expected = 0..n;
while !expected.is_empty() {
assert_eq!(it.len(), expected.len());
assert_eq!(it.next(), expected.next());
assert_eq!(it.len(), expected.len());
assert_eq!(it.next_back(), expected.next_back());
}
assert_eq!(it.len(), 0);
assert_eq!(it.next(), None);
assert_eq!(it.next_back(), None);
}
#[test]
fn test_spectrum_iterator_remaining() {
use crate::spectrum::SpectrumLike;
for n in 0..6 {
let mut source = spectra(n);
let mut it = source.iter();
check_remaining(it.by_ref().map(|s| s.index()), n);
assert_eq!(SpectrumSource::len(&it), n);
it.reset();
assert_eq!(ExactSizeIterator::len(&it), n);
assert!(it.nth(usize::MAX).is_none());
assert!(it.next_back().is_none());
if n > 0 {
it.start_from_index(n - 1).unwrap();
assert_eq!(ExactSizeIterator::len(&it), 1);
assert_eq!(it.next().unwrap().index(), n - 1);
it.start_from_id("0").unwrap();
assert_eq!(it.next().unwrap().index(), 0);
assert!(it.nth(usize::MAX).is_none());
}
assert!(it.start_from_index(n).is_err());
}
}
#[test]
fn test_frame_iterator_remaining() {
use crate::spectrum::IonMobilityFrameLike;
use mzpeaks::{CentroidPeak, DeconvolutedPeak};
for n in 0..6 {
let mut source =
Generic3DIonMobilityFrameSource::<CentroidPeak, DeconvolutedPeak, _>::new(spectra(
n,
));
let mut it = source.iter();
check_remaining(it.by_ref().map(|s| s.index()), n);
assert_eq!(IonMobilityFrameSource::len(&it), n);
it.reset();
assert_eq!(ExactSizeIterator::len(&it), n);
assert!(it.nth(usize::MAX).is_none());
assert!(it.next_back().is_none());
if n > 0 {
it.start_from_index(n - 1).unwrap();
assert_eq!(ExactSizeIterator::len(&it), 1);
assert_eq!(it.next().unwrap().index(), n - 1);
it.start_from_id("0").unwrap();
assert_eq!(it.next().unwrap().index(), 0);
assert!(it.nth(usize::MAX).is_none());
}
assert!(it.start_from_index(n).is_err());
}
}
#[test]
fn test_chromatogram_iterator_remaining() {
use crate::spectrum::Chromatogram;
struct Chromatograms {
count: usize,
fail: bool,
calls: usize,
}
impl ChromatogramSource for Chromatograms {
fn get_chromatogram_by_id(&mut self, _: &str) -> Option<Chromatogram> {
None
}
fn get_chromatogram_by_index(&mut self, index: usize) -> Option<Chromatogram> {
self.calls += 1;
let fail = std::mem::take(&mut self.fail);
(index < self.count && !fail).then(Chromatogram::default)
}
fn count_chromatograms(&self) -> usize {
self.count
}
}
for (count, fail) in [(0, false), (1, false), (3, false), (3, true)] {
let mut source = Chromatograms {
count,
fail,
calls: 0,
};
let mut it = source.iter_chromatograms().fuse();
for remaining in (1..=count).rev() {
assert_eq!(it.len(), remaining);
if fail {
break;
}
assert!(it.next().is_some());
}
assert!(it.next().is_none());
assert_eq!(it.len(), 0);
assert!(it.next().is_none());
assert_eq!(source.calls, if fail { 1 } else { count });
}
}
}