mod inference;
mod dispatch;
mod pipeline;
pub use dispatch::{MZReader, MZReaderType, MZReaderBuilder, IMMZReaderType};
#[cfg(feature = "async_partial")]
pub use dispatch::{AsyncMZReaderType, AsyncMZReader, AsyncMZReaderBuilder};
pub use inference::{infer_from_path, infer_from_stream, infer_format, MassSpectrometryFormat, _SourceFileExt};
pub use pipeline::{MassSpectrometryReadWriteProcess, Source, Sink};
#[cfg(test)]
mod test {
use std::{fs, io, path};
use mzpeaks::{CentroidPeak, DeconvolutedPeak};
use crate::{
prelude::*,
spectrum::{ArrayType, Spectrum},
io::DetailLevel
};
use super::*;
#[cfg(feature = "mzml")]
#[test]
fn infer_mzml() {
let path = path::Path::new("./test/data/small.mzML");
assert!(path.exists());
let (fmt, zipped) = infer_from_path(path);
assert_eq!(fmt, MassSpectrometryFormat::MzML);
assert!(!zipped);
}
#[test]
fn infer_mgf() {
let path = path::Path::new("./test/data/small.mgf");
assert!(path.exists());
let (fmt, zipped) = infer_from_path(path);
assert_eq!(fmt, MassSpectrometryFormat::MGF);
assert!(!zipped);
}
#[cfg(feature = "thermo")]
#[test]
fn infer_thermo() {
let path = path::Path::new("./test/data/small.RAW");
let (fmt, zipped) = infer_from_path(path);
assert_eq!(fmt, MassSpectrometryFormat::ThermoRaw);
assert!(!zipped);
}
#[cfg(feature = "mzml")]
#[test]
fn infer_open() {
let path = path::Path::new("./test/data/small.mzML");
assert!(path.exists());
if let Ok(mut reader) = MZReader::open_path(path) {
assert_eq!(reader.len(), 48);
assert_eq!(*reader.detail_level(), DetailLevel::Full);
if let Some(spec) = reader.get_spectrum_by_index(10) {
let spec: Spectrum = spec;
assert!(spec.index() == 10);
assert!(spec.id() == "controllerType=0 controllerNumber=1 scan=11");
if let Some(data_arrays) = &spec.arrays {
assert!(data_arrays.has_array(&ArrayType::MZArray));
assert!(data_arrays.has_array(&ArrayType::IntensityArray));
let mzs = data_arrays.mzs().unwrap();
assert!(mzs.len() == 941);
}
} else {
panic!("Failed to retrieve spectrum by index")
}
assert_eq!(reader.get_spectrum_by_id("controllerType=0 controllerNumber=1 scan=11").unwrap().index(), 10);
if let Some(spec) = reader.get_spectrum_by_time(0.358558333333) {
assert_eq!(spec.index(), 34);
} else {
panic!("Failed to retrieve spectrum by time")
}
} else {
panic!("Failed to open file")
}
}
#[cfg(feature = "thermo")]
#[test]
fn infer_open_thermo() {
let path = path::Path::new("./test/data/small.RAW");
assert!(path.exists());
if let Ok(mut reader) = MZReader::open_path(path) {
assert_eq!(reader.len(), 48);
assert_eq!(*reader.detail_level(), DetailLevel::Full);
if let Some(spec) = reader.get_spectrum_by_index(10) {
let spec: Spectrum = spec;
assert_eq!(spec.index(), 10);
assert_eq!(spec.id(), "controllerType=0 controllerNumber=1 scan=11");
assert_eq!(spec.peaks().len(), 941);
} else {
panic!("Failed to retrieve spectrum by index")
}
assert_eq!(reader.get_spectrum_by_id("controllerType=0 controllerNumber=1 scan=11").unwrap().index(), 10);
if let Some(spec) = reader.get_spectrum_by_time(0.358558333333) {
assert_eq!(spec.index(), 34);
} else {
panic!("Failed to retrieve spectrum by time")
}
} else {
panic!("Failed to open file")
}
}
#[test]
fn test_source_conv() -> io::Result<()> {
let s = Source::<CentroidPeak, DeconvolutedPeak>::from("text/path".as_ref());
assert!(matches!(s, Source::PathLike(_)));
let fh = Box::new(io::BufReader::new(fs::File::open("./test/data/small.mgf")?)) as Box<dyn SeekRead + Send>;
let rs: Source<CentroidPeak, DeconvolutedPeak> = (fh, MassSpectrometryFormat::MGF).into();
assert!(matches!(rs, Source::Reader(_, _)));
Ok(())
}
#[cfg(feature = "mzml")]
#[test]
fn test_dispatch_mzreader() -> io::Result<()> {
let mut reader = MZReader::open_path("./test/data/small.mzML")?;
let n = reader.len();
let n_ms1 = reader.iter().filter(|s| s.ms_level() == 1).count();
let n_msn = reader.iter().filter(|s| s.ms_level() == 2).count();
assert_eq!(n, 48);
assert_eq!(n, n_ms1 + n_msn);
Ok(())
}
#[cfg(feature = "mzml")]
#[test]
fn test_infer_stream() -> io::Result<()> {
let mut mzml_file = fs::File::open("./test/data/small.mzML")?;
let (form, gzip) = infer_from_stream(&mut mzml_file)?;
assert_eq!(form, MassSpectrometryFormat::MzML);
assert!(!gzip);
mzml_file = fs::File::open("./test/data/20200204_BU_8B8egg_1ug_uL_7charges_60_min_Slot2-11_1_244.mzML.gz")?;
let (form, gzip) = infer_from_stream(&mut mzml_file)?;
assert_eq!(form, MassSpectrometryFormat::MzML);
assert!(gzip);
let mut mgf_file = fs::File::open("./test/data/small.mgf")?;
let (form, gzip) = infer_from_stream(&mut mgf_file)?;
assert_eq!(form, MassSpectrometryFormat::MGF);
assert!(!gzip);
Ok(())
}
#[cfg(feature = "mgf")]
#[test]
fn test_infer_short_prefixes() -> io::Result<()> {
use io::{Read, Seek, Write};
let input = b"BEGIN IONS\nEND IONS\n";
let mut encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
encoder.write_all(input)?;
let compressed = encoder.finish()?;
for (bytes, gzipped) in [
(input.to_vec(), false),
(compressed.clone(), true),
([compressed.clone(), compressed].concat(), true),
] {
let source = io::Cursor::new(&bytes[..1]).chain(io::Cursor::new(&bytes[1..]));
let mut stream = crate::io::PreBufferedStream::new_with_buffer_size(source, 128)?;
assert_eq!(
infer_from_stream(&mut stream)?,
(MassSpectrometryFormat::MGF, gzipped)
);
assert_eq!(stream.stream_position()?, 0);
let mut remaining = Vec::new();
stream.read_to_end(&mut remaining)?;
assert_eq!(remaining, bytes);
}
#[cfg(feature = "mzml")]
for prefix in [b"".as_slice(), b" \n", b"\xef\xbb\xbf"] {
let bytes = [prefix, b"<!--BEGIN IONS--><mzML></mzML>"].concat();
let split = prefix.len() + 14;
let source = io::Cursor::new(&bytes[..1]).chain(io::Cursor::new(&bytes[1..split]));
let source = source.chain(io::Cursor::new(&bytes[split..]));
let mut stream = crate::io::PreBufferedStream::new(source)?;
assert_eq!(
infer_from_stream(&mut stream)?,
(MassSpectrometryFormat::MzML, false)
);
}
Ok(())
}
#[test]
fn test_infer_restores_position() -> io::Result<()> {
for bytes in [vec![], vec![b'x'], vec![0; 700]] {
let mut stream = io::Cursor::new(bytes);
stream.set_position(1);
assert_eq!(
infer_from_stream(&mut stream)?,
(MassSpectrometryFormat::Unknown, false)
);
assert_eq!(stream.position(), 1);
}
for bytes in [vec![0x1f, 0x8b], vec![0x1f, 0x8b, 8, 0]] {
let mut stream = io::Cursor::new([vec![b'x'], bytes].concat());
stream.set_position(1);
assert!(infer_from_stream(&mut stream).is_err());
assert_eq!(stream.position(), 1);
}
Ok(())
}
#[cfg(feature = "mzml")]
#[test]
fn test_open_streams_preserve_spectra() -> io::Result<()> {
use io::Read;
fn check(
actual: impl Iterator<Item = Spectrum> + MSDataFileMetadata,
expected: impl Iterator<Item = Spectrum> + MSDataFileMetadata,
) {
assert_eq!(actual.file_description(), expected.file_description());
assert_eq!(
actual.instrument_configurations(),
expected.instrument_configurations()
);
let actual: Vec<_> = actual.collect();
let expected: Vec<_> = expected.collect();
assert_eq!(actual.len(), 48);
assert_eq!(actual.len(), expected.len());
for (actual, expected) in actual.iter().zip(&expected) {
assert_eq!(actual.description, expected.description);
let actual = actual.arrays.as_ref().unwrap();
let expected = expected.arrays.as_ref().unwrap();
assert_eq!(actual.len(), expected.len());
for (name, expected) in expected.iter() {
let actual = actual.get(name).unwrap();
assert_eq!(actual.name, expected.name);
assert_eq!(actual.dtype, expected.dtype);
assert_eq!(actual.compression, expected.compression);
assert_eq!(actual.unit, expected.unit);
assert_eq!(actual.params, expected.params);
assert_eq!(actual.data, expected.data);
}
}
}
for (path, gzipped) in [
("test/data/small.mzML", false),
("test/data/small.mzML.gz", true),
] {
let input = fs::read(path)?;
let decoded = if gzipped {
let mut decoded = Vec::new();
flate2::read::GzDecoder::new(io::Cursor::new(&input)).read_to_end(&mut decoded)?;
decoded
} else {
input.clone()
};
let expected = crate::MzMLReader::new(io::Cursor::new(decoded.clone()));
let source = io::Cursor::new(&input[..2]).chain(io::Cursor::new(&input[2..]));
if gzipped {
check(MZReader::open_gzipped_read(source)?, expected);
} else {
check(MZReader::open_read(source)?, expected);
}
let source = Box::new(io::Cursor::new(input));
check(
MZReader::<crate::io::PreBufferedStream<fs::File>>::open_read_generic(source)?,
crate::MzMLReader::new(io::Cursor::new(decoded)),
);
}
Ok(())
}
#[cfg(feature = "imzml")]
#[test]
fn test_infer_short_imzml_prefix() -> io::Result<()> {
use io::Read;
let bytes = b"<mzML><cvList><cv id=\"IMS\"/></cvList></mzML>";
let source = io::Cursor::new(&bytes[..6]).chain(io::Cursor::new(&bytes[6..]));
let mut stream = crate::io::PreBufferedStream::new(source)?;
assert_eq!(
infer_from_stream(&mut stream)?,
(MassSpectrometryFormat::IMzML, false)
);
Ok(())
}
}