use std::{io, marker::PhantomData, path::{Path, PathBuf}};
#[allow(unused)]
use mzpeaks::{feature::{ChargedFeature, Feature}, IonMobility, Mass, MZ};
#[allow(unused)]
use crate::{prelude::*, spectrum::{CentroidPeakAdapting, DeconvolutedPeakAdapting}};
#[allow(unused)]
use super::{Sink, Source, SpectrumSource, infer_format::MZReaderType};
#[doc(hidden)]
#[cfg(feature = "mgf")]
pub use super::mgf::{MGFReaderType, MGFWriterType};
#[doc(hidden)]
#[cfg(not(feature = "mgf"))]
pub use super::infer_format::MZReaderType as MGFReaderType;
#[doc(hidden)]
#[cfg(not(feature = "mgf"))]
pub type MGFWriterType<W, C, D> = DummyWriter<W, C, D>;
#[doc(hidden)]
#[cfg(feature = "mzml")]
pub use super::mzml::{MzMLReaderType, MzMLWriterType};
#[doc(hidden)]
#[cfg(not(feature = "mzml"))]
pub use super::infer_format::MZReaderType as MzMLReaderType;
#[cfg(not(feature = "mzml"))]
pub type MzMLWriterType<W, C, D> = DummyWriter<W, C, D>;
#[doc(hidden)]
#[cfg(feature = "thermo")]
pub use super::thermo::ThermoRawReaderType;
#[doc(hidden)]
#[cfg(not(feature = "thermo"))]
pub type ThermoRawReaderType<C, D> = MZReaderType<std::fs::File, C, D>;
#[doc(hidden)]
#[cfg(feature = "mzmlb")]
pub use super::mzmlb::{MzMLbReaderType, MzMLbWriterType};
#[doc(hidden)]
#[cfg(not(feature = "mzmlb"))]
pub type MzMLbReaderType<C, D> = MZReaderType<std::fs::File, C, D>;
#[doc(hidden)]
#[cfg(not(feature = "mzmlb"))]
pub type MzMLbWriterType<C, D> = DummyWriter2<C, D>;
#[doc(hidden)]
#[cfg(feature = "bruker_tdf")]
use super::tdf::TDFSpectrumReaderType as _TDFSpectrumReaderType;
#[doc(hidden)]
#[cfg(feature = "bruker_tdf")]
pub type TDFSpectrumReaderType<C, D> = _TDFSpectrumReaderType<Feature<MZ, IonMobility>, ChargedFeature<Mass, IonMobility>, C, D>;
#[doc(hidden)]
#[cfg(not(feature = "bruker_tdf"))]
pub type TDFSpectrumReaderType<C, D> = MZReaderType<std::fs::File, C, D>;
pub const fn mzml_support() -> bool {
#[cfg(feature = "mzml")]
return true;
#[cfg(not(feature = "mzml"))]
return false;
}
pub const fn mgf_support() -> bool {
#[cfg(feature = "mgf")]
return true;
#[cfg(not(feature = "mgf"))]
return false;
}
pub const fn mzmlb_support() -> bool {
#[cfg(feature = "mzmlb")]
return true;
#[cfg(not(feature = "mzmlb"))]
return false;
}
pub const fn thermo_support() -> bool {
#[cfg(feature = "thermo")]
return true;
#[cfg(not(feature = "thermo"))]
return false;
}
pub const fn bruker_tdf_support() -> bool {
#[cfg(feature = "bruker_tdf")]
return true;
#[cfg(not(feature = "bruker_tdf"))]
return false;
}
#[doc(hidden)]
#[allow(unused)]
pub fn mgf_new<R: io::Read + io::Seek, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdapting + BuildFromArrayMap>(handle: R) -> MGFReaderType<R, C, D> {
#[cfg(feature = "mgf")]
return MGFReaderType::new(handle);
#[cfg(not(feature = "mgf"))]
panic!("MGF reading is not enabled")
}
#[doc(hidden)]
#[allow(unused)]
pub fn mgf_new_indexed<R: io::Read + io::Seek, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdapting + BuildFromArrayMap>(handle: R) -> MGFReaderType<R, C, D> {
#[cfg(not(feature = "mgf"))]
panic!("MGF reading is not enabled");
#[cfg(feature = "mgf")]
return MGFReaderType::new_indexed(handle);
}
#[doc(hidden)]
#[allow(unused)]
pub fn mzml_new<R: io::Read + io::Seek, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdapting + BuildFromArrayMap>(handle: R) -> MzMLReaderType<R, C, D> {
#[cfg(feature = "mzml")]
return MzMLReaderType::new(handle);
#[cfg(not(feature = "mzml"))]
panic!("mzML reading is not enabled")
}
#[doc(hidden)]
#[allow(unused)]
pub fn mzml_new_indexed<R: io::Read + io::Seek, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdapting + BuildFromArrayMap>(handle: R) -> MzMLReaderType<R, C, D> {
#[cfg(feature = "mzml")]
return MzMLReaderType::new_indexed(handle);
#[cfg(not(feature = "mzml"))]
panic!("mzML reading is not enabled")
}
#[doc(hidden)]
#[allow(unused)]
pub fn thermo_new<R: Into<PathBuf>, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdapting + BuildFromArrayMap>(handle: R) -> io::Result<ThermoRawReaderType<C ,D>> {
#[cfg(feature = "thermo")]
return ThermoRawReaderType::new(handle);
#[cfg(not(feature = "thermo"))]
panic!("Thermo RAW file reading not enabled")
}
#[doc(hidden)]
#[allow(unused)]
pub fn mzmlb_new<R: AsRef<Path>, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdapting + BuildFromArrayMap>(handle: R) -> io::Result<MzMLbReaderType<C ,D>> {
#[cfg(feature = "mzmlb")]
return MzMLbReaderType::new(&handle);
#[cfg(not(feature = "mzmlb"))]
panic!("mzMLb file reading not enabled")
}
#[doc(hidden)]
#[allow(unused)]
pub fn bruker_tdf_new<R: AsRef<Path>, C: CentroidPeakAdapting + BuildFromArrayMap, D: DeconvolutedPeakAdapting + BuildFromArrayMap>(handle: R) -> io::Result<TDFSpectrumReaderType<C ,D>> {
#[cfg(feature = "bruker_tdf")]
return TDFSpectrumReaderType::new(handle).map_err(|e| io::Error::new( io::ErrorKind::Other, e));
#[cfg(not(feature = "bruker_tdf"))]
panic!("Bruker TDF file reading not enabled")
}
#[doc(hidden)]
pub struct DummyWriter<W, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting>(PhantomData<(W, C, D)>);
impl<W, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> MSDataFileMetadata for DummyWriter<W, C, D> {
fn data_processings(&self) -> &Vec<crate::meta::DataProcessing> {
panic!("stub");
}
fn instrument_configurations(&self) -> &std::collections::HashMap<u32, crate::meta::InstrumentConfiguration> {
panic!("stub");
}
fn file_description(&self) -> &crate::meta::FileDescription {
panic!("stub");
}
fn softwares(&self) -> &Vec<crate::meta::Software> {
panic!("stub");
}
fn samples(&self) -> &Vec<crate::meta::Sample> {
panic!("stub");
}
fn data_processings_mut(&mut self) -> &mut Vec<crate::meta::DataProcessing> {
panic!("stub");
}
fn instrument_configurations_mut(&mut self) -> &mut std::collections::HashMap<u32, crate::meta::InstrumentConfiguration> {
panic!("stub");
}
fn file_description_mut(&mut self) -> &mut crate::meta::FileDescription {
panic!("stub");
}
fn softwares_mut(&mut self) -> &mut Vec<crate::meta::Software> {
panic!("stub");
}
fn samples_mut(&mut self) -> &mut Vec<crate::meta::Sample> {
panic!("stub");
}
}
impl<W, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> DummyWriter<W, C, D> {
pub fn new(_: W) -> Self {
Self(PhantomData)
}
}
impl<W, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> SpectrumWriter<C, D> for DummyWriter<W, C, D> {
#[allow(unused)]
fn write<S: SpectrumLike<C, D> + 'static>(&mut self, spectrum: &S) -> io::Result<usize> {
Err(io::Error::new(io::ErrorKind::Unsupported, "Dummy Writer"))
}
fn flush(&mut self) -> io::Result<()> {
Err(io::Error::new(io::ErrorKind::Unsupported, "Dummy Writer"))
}
fn close(&mut self) -> io::Result<()> {
Err(io::Error::new(io::ErrorKind::Unsupported, "Dummy Writer"))
}
}
#[doc(hidden)]
pub struct DummyWriter2<C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting>(PhantomData<(C, D)>);
impl<C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> MSDataFileMetadata for DummyWriter2<C, D> {
fn data_processings(&self) -> &Vec<crate::meta::DataProcessing> {
todo!()
}
fn instrument_configurations(&self) -> &std::collections::HashMap<u32, crate::meta::InstrumentConfiguration> {
todo!()
}
fn file_description(&self) -> &crate::meta::FileDescription {
todo!()
}
fn softwares(&self) -> &Vec<crate::meta::Software> {
todo!()
}
fn samples(&self) -> &Vec<crate::meta::Sample> {
todo!()
}
fn data_processings_mut(&mut self) -> &mut Vec<crate::meta::DataProcessing> {
todo!()
}
fn instrument_configurations_mut(&mut self) -> &mut std::collections::HashMap<u32, crate::meta::InstrumentConfiguration> {
todo!()
}
fn file_description_mut(&mut self) -> &mut crate::meta::FileDescription {
todo!()
}
fn softwares_mut(&mut self) -> &mut Vec<crate::meta::Software> {
todo!()
}
fn samples_mut(&mut self) -> &mut Vec<crate::meta::Sample> {
todo!()
}
}
impl<C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> DummyWriter2<C, D> {
#[allow(unused)]
pub fn new<P>(path: P) -> io::Result<Self> {
Ok(Self(PhantomData))
}
}
impl<C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> SpectrumWriter<C, D> for DummyWriter2<C, D> {
#[allow(unused)]
fn write<S: SpectrumLike<C, D> + 'static>(&mut self, spectrum: &S) -> io::Result<usize> {
Err(io::Error::new(io::ErrorKind::Unsupported, "Dummy Writer"))
}
fn flush(&mut self) -> io::Result<()> {
Err(io::Error::new(io::ErrorKind::Unsupported, "Dummy Writer"))
}
fn close(&mut self) -> io::Result<()> {
Err(io::Error::new(io::ErrorKind::Unsupported, "Dummy Writer"))
}
}
#[macro_export]
macro_rules! mz_read {
($source:expr, $reader:ident => $impl:tt) => {
$crate::mz_read!($source, $reader => $impl, $crate::mzpeaks::CentroidPeak, $crate::mzpeaks::DeconvolutedPeak)
};
($source:expr, $reader:ident => $impl:tt, $C:ty, $D:ty) => {{
let source = $crate::io::Source::<_, _>::from($source);
#[allow(unused_mut)]
let mut expr = || -> io::Result<_> {
match source {
$crate::io::Source::PathLike(read_path) => {
let (format, is_gzipped) = $crate::io::infer_format(&read_path)?;
match format {
$crate::io::MassSpectrometryFormat::MGF if $crate::io::_impl::mgf_support() => {
let handle = std::fs::File::open(read_path)?;
if is_gzipped {
let fh = $crate::io::RestartableGzDecoder::new(std::io::BufReader::new(handle));
#[allow(unused_mut)]
let mut $reader: $crate::io::StreamingSpectrumIterator<$C, $D, _, _> = $crate::io::StreamingSpectrumIterator::new($crate::io::_impl::mgf_new::<_, $C, $D>(fh));
Ok($impl)
} else {
#[allow(unused_mut)]
let mut $reader: $crate::io::_impl::MGFReaderType<_, $C, $D> = $crate::io::_impl::mgf_new_indexed(handle);
Ok($impl)
}
}
$crate::io::MassSpectrometryFormat::MzML if $crate::io::_impl::mzml_support() => {
let handle = std::fs::File::open(read_path)?;
if is_gzipped {
let fh = $crate::io::RestartableGzDecoder::new(std::io::BufReader::new(handle));
#[allow(unused_mut)]
let mut $reader: $crate::io::StreamingSpectrumIterator<$C, $D, _, _> = $crate::io::StreamingSpectrumIterator::new($crate::io::_impl::mzml_new::<_, $C, $D>(fh));
Ok($impl)
} else {
#[allow(unused_mut)]
let mut $reader: $crate::io::mzml::MzMLReaderType<_, $C, $D> = $crate::io::_impl::mzml_new_indexed::<_, $C, $D>(handle);
Ok($impl)
}
}
$crate::io::MassSpectrometryFormat::MzMLb if $crate::io::_impl::mzmlb_support() => {
#[allow(unused_mut)]
let mut $reader: $crate::io::_impl::MzMLbReaderType<$C, $D> = $crate::io::_impl::mzmlb_new(&read_path)?;
Ok($impl)
},
$crate::io::MassSpectrometryFormat::ThermoRaw if $crate::io::_impl::thermo_support() => {
#[allow(unused_mut)]
let mut $reader: $crate::io::_impl::ThermoRawReaderType<$C, $D> = $crate::io::_impl::thermo_new::<_, $C, $D>(&read_path)?;
Ok($impl)
},
$crate::io::MassSpectrometryFormat::BrukerTDF if $crate::io::_impl::bruker_tdf_support() => {
#[allow(unused_mut)]
let mut $reader: $crate::io::_impl::TDFSpectrumReaderType<$C, $D> = $crate::io::_impl::bruker_tdf_new(read_path)?;
Ok($impl)
}
_ => Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
format!(
"Input file format for {} not supported",
read_path.display()
),
)),
}
},
$crate::io::Source::Reader(mut handle, format) => {
let (format, is_gzipped) = if let Some(format) = format { (format, false) } else { $crate::io::infer_from_stream(&mut handle)? };
match format {
$crate::io::MassSpectrometryFormat::MGF if $crate::io::_impl::mgf_support() => {
let handle = std::io::BufReader::new(handle);
#[allow(unused_mut)]
if is_gzipped {
let fh = $crate::io::RestartableGzDecoder::new(std::io::BufReader::new(handle));
#[allow(unused_mut)]
let mut $reader: $crate::io::StreamingSpectrumIterator<$C, $D, _, _> = $crate::io::StreamingSpectrumIterator::new($crate::io::_impl::mgf_new::<_, $C, $D>(fh));
Ok($impl)
} else {
#[allow(unused_mut)]
let mut $reader: $crate::io::_impl::MGFReaderType<_, $C, $D> = $crate::io::_impl::mgf_new_indexed(handle);
Ok($impl)
}
},
$crate::io::MassSpectrometryFormat::MzML if $crate::io::_impl::mzml_support() => {
let handle = std::io::BufReader::new(handle);
#[allow(unused_mut)]
if is_gzipped {
let fh = $crate::io::RestartableGzDecoder::new(std::io::BufReader::new(handle));
#[allow(unused_mut)]
let mut $reader: $crate::io::StreamingSpectrumIterator<$C, $D, _, _> = $crate::io::StreamingSpectrumIterator::new($crate::io::_impl::mzml_new::<_, $C, $D>(fh));
Ok($impl)
} else {
#[allow(unused_mut)]
let mut $reader: $crate::io::mzml::MzMLReaderType<_, $C, $D> = $crate::io::_impl::mzml_new_indexed::<_, $C, $D>(handle);
Ok($impl)
}
},
_ => Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
format!(
"Input file format for {:?} not supported from an io::Read",
format
),
)),
}
},
$crate::io::Source::Receiver(receiver) => {
#[allow(unused_mut)]
let mut $reader: $crate::io::StreamingSpectrumIterator<$C, $D, _, _> = $crate::io::StreamingSpectrumIterator::new(receiver);
Ok($impl)
},
$crate::io::Source::Stdin => {
let mut buffered =
$crate::io::PreBufferedStream::new_with_buffer_size(std::io::stdin(), 2usize.pow(20))?;
let (ms_format, compressed) = $crate::io::infer_from_stream(&mut buffered)?;
match ms_format {
$crate::io::MassSpectrometryFormat::MGF if $crate::io::_impl::mgf_support() => {
if compressed {
#[allow(unused_mut)]
let mut $reader: $crate::io::StreamingSpectrumIterator<$C, $D, _, _> = $crate::io::StreamingSpectrumIterator::new(
$crate::io::_impl::mgf_new(
$crate::io::RestartableGzDecoder::new(std::io::BufReader::new(buffered)),
));
Ok($impl)
} else {
#[allow(unused_mut)]
let mut $reader: $crate::io::StreamingSpectrumIterator<$C, $D, _, _> = $crate::io::StreamingSpectrumIterator::new(
$crate::io::_impl::mgf_new(buffered));
Ok($impl)
}
}
$crate::io::MassSpectrometryFormat::MzML if $crate::io::_impl::mzml_support() => {
if compressed {
#[allow(unused_mut)]
let mut $reader: $crate::io::StreamingSpectrumIterator<$C, $D, _, _> = $crate::io::StreamingSpectrumIterator::new(
$crate::io::_impl::mzml_new($crate::io::RestartableGzDecoder::new(std::io::BufReader::new(buffered)),
));
Ok($impl)
} else {
#[allow(unused_mut)]
let mut $reader: $crate::io::StreamingSpectrumIterator<$C, $D, _, _> = $crate::io::StreamingSpectrumIterator::new(
$crate::io::_impl::mzml_new(buffered));
Ok($impl)
}
}
_ => {
return Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"{ms_format:?} format is not supported over Stdin",
).into())
}
}
},
}
};
expr()
}};
}
#[macro_export]
macro_rules! mz_write {
($sink:expr, $writer:ident => $impl:tt) => {
mz_write!($sink, $writer => $impl, $crate::mzpeaks::CentroidPeak, $crate::mzpeaks::DeconvolutedPeak)
};
($sink:expr, $writer:ident => $impl:tt, $C:ty, $D:ty) => {{
let sink = $crate::io::Sink::<$C, $D>::from($sink);
let mut expr = || -> io::Result<_> {
match sink {
$crate::io:: Sink::Sender(_) | $crate::io::Sink::SyncSender(_) => {
Err(std::io::Error::new(std::io::ErrorKind::Unsupported, "Sender writers aren't supported by `mz_write`"))
}
$crate::io::Sink::PathLike(write_path) => {
let (writer_format, is_gzip) = $crate::io::infer_from_path(&write_path);
match writer_format {
$crate::io::MassSpectrometryFormat::MGF if $crate::io::_impl::mgf_support() => {
let handle = std::io::BufWriter::new(std::fs::File::create(&write_path)?);
if is_gzip {
let handle = flate2::write::GzEncoder::new(handle, flate2::Compression::best());
let mut $writer: $crate::io::_impl::MGFWriterType<_, $C, $D> = $crate::io::_impl::MGFWriterType::new(
handle,
);
Ok($impl)
} else {
let mut $writer: $crate::io::_impl::MGFWriterType<_, $C, $D> = $crate::io::_impl::MGFWriterType::new(
handle,
);
Ok($impl)
}
}
$crate::io::MassSpectrometryFormat::MzML if $crate::io::_impl::mzml_support() => {
let handle = std::io::BufWriter::new(std::fs::File::create(&write_path)?);
if is_gzip {
let handle = flate2::write::GzEncoder::new(handle, flate2::Compression::best());
let mut $writer: $crate::io::_impl::MzMLWriterType<_, $C, $D> = $crate::io::_impl::MzMLWriterType::new(
handle,
);
Ok($impl)
} else {
let mut $writer: $crate::io::_impl::MzMLWriterType<_, $C, $D> = $crate::io::_impl::MzMLWriterType::new(
handle,
);
Ok($impl)
}
}
$crate::io::MassSpectrometryFormat::MzMLb if $crate::io::_impl::mzmlb_support() => {
let mut $writer = $crate::io::_impl::MzMLbWriterType::<$C, $D>::new(&write_path)?;
Ok($impl)
}
_ => Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
format!(
"Output file format {:?} for {} not supported",
writer_format,
write_path.display()
),
)),
}
},
$crate::io::Sink::Writer(handle, writer_format) => {
match writer_format {
$crate::io::MassSpectrometryFormat::MGF if $crate::io::_impl::mgf_support() => {
let handle = std::io::BufWriter::new(handle);
let mut $writer: $crate::io::_impl::MGFWriterType<_, $C, $D> = $crate::io::_impl::MGFWriterType::new(
handle,
);
Ok($impl)
}
$crate::io::MassSpectrometryFormat::MzML if $crate::io::_impl::mzml_support() => {
let handle = std::io::BufWriter::new(handle);
let mut $writer: $crate::io::_impl::MzMLWriterType<_, $C, $D> = $crate::io::_impl::MzMLWriterType::new(
handle,
);
Ok($impl)
}
_ => {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
format!(
"Output file format for {:?} not supported",
writer_format
),
))
}
}
}
}
};
expr()
}};
}
#[cfg(test)]
mod test {
use crate::prelude::*;
use std::io;
#[test]
fn test_mz_read() -> io::Result<()> {
let val: Vec<_> =
mz_read!("./test/data/small.mzML".as_ref(), reader => { reader.collect() })?;
assert_eq!(val.len(), 48);
let val: Vec<_> =
mz_read!("./test/data/small.mgf".as_ref(), reader => { reader.collect() })?;
assert_eq!(val.len(), 35);
let val = mz_read!("./test/data/small.mzML".as_ref(), reader => { reader.file_description().clone() })?;
assert_eq!(val.source_files.len(), 1);
Ok(())
}
#[test]
fn test_mz_read_nested() -> io::Result<()> {
mz_read!("./test/data/small.mzML".as_ref(), reader => {
mz_read!("./test/data/small.mzML".as_ref(), reader2 => {
assert_eq!(reader.len(), reader2.len());
})?;
})?;
Ok(())
}
#[test]
fn test_mz_write() -> io::Result<()> {
let tmpdir = tempfile::tempdir()?;
let path = tmpdir.path().join("test.mzML");
mz_read!("./test/data/small.mzML".as_ref(), reader => {
mz_write!(path.as_ref(), writer => {
writer.copy_metadata_from(&reader);
for s in reader {
writer.write_owned(s)?;
}
})?;
})?;
Ok(())
}
}