use std::convert::TryFrom;
use std::io;
use std::marker::PhantomData;
use log::warn;
use thiserror::Error;
use mzpeaks::{
feature::{ChargedFeature, Feature, FeatureLike},
DeconvolutedCentroidLike, IonMobility, KnownCharge, Mass, MZ,
CentroidLike,
};
use crate::spectrum::group::IonMobilityFrameGroupingIterator;
use crate::spectrum::spectrum_types::MultiLayerSpectrum;
use crate::spectrum::{
IonMobilityFrameLike,
MultiLayerIonMobilityFrame,
};
use crate::{
io::{DetailLevel, OffsetIndex},
prelude::{MSDataFileMetadata, SpectrumLike},
spectrum::{HasIonMobility, IonMobilityFrameGroup},
};
use super::{
IonMobilityFrameGrouping, RandomAccessSpectrumIterator, SpectrumAccessError, SpectrumSource,
};
pub trait IonMobilityFrameSource<
C: FeatureLike<MZ, IonMobility> = Feature<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge = ChargedFeature<Mass, IonMobility>,
S: IonMobilityFrameLike<C, D> = MultiLayerIonMobilityFrame<C, D>,
>: Iterator<Item = S>
{
fn reset(&mut self);
fn detail_level(&self) -> &DetailLevel;
fn set_detail_level(&mut self, detail_level: DetailLevel);
fn get_frame_by_id(&mut self, id: &str) -> Option<S>;
fn get_frame_by_index(&mut self, index: usize) -> Option<S>;
fn get_frame_by_time(&mut self, time: f64) -> Option<S> {
let n = self.len();
let mut lo: usize = 0;
let mut hi: usize = n;
let mut best_error: f64 = f64::INFINITY;
let mut best_match: Option<S> = None;
if lo == hi {
return None;
}
while hi != lo {
let mid = (hi + lo) / 2;
let scan = self.get_frame_by_index(mid)?;
let scan_time = scan.start_time();
let err = (scan_time - time).abs();
if err < best_error {
best_error = err;
best_match = Some(scan);
}
if hi.saturating_sub(1) == lo {
return best_match;
} else if scan_time > time {
hi = mid;
} else {
lo = mid;
}
}
best_match
}
fn len(&self) -> usize {
self.get_index().len()
}
fn is_empty(&self) -> bool {
self.len() == 0
}
fn get_index(&self) -> &OffsetIndex;
fn set_index(&mut self, index: OffsetIndex);
fn _offset_of_id(&self, id: &str) -> Option<u64> {
self.get_index().get(id)
}
fn _offset_of_index(&self, index: usize) -> Option<u64> {
self.get_index()
.get_index(index)
.map(|(_id, offset)| offset)
}
fn _offset_of_time(&mut self, time: f64) -> Option<u64> {
match self.get_frame_by_time(time) {
Some(scan) => self._offset_of_index(scan.index()),
None => None,
}
}
fn get_group_by_index(&mut self, index: usize) -> Option<IonMobilityFrameGroup<C, D, S>>
where
Self: Sized
{
self.groups().nth(index)
}
fn iter(&mut self) -> IonMobilityFrameIterator<'_, C, D, S, Self>
where
Self: Sized,
{
IonMobilityFrameIterator::new(self)
}
fn groups(
&mut self,
) -> IonMobilityFrameGroupingIterator<IonMobilityFrameIterator<'_, C, D, S, Self>, C, D, S>
where
Self: Sized,
{
IonMobilityFrameGroupingIterator::new(self.iter())
}
fn into_groups(self) -> IonMobilityFrameGroupingIterator<Self, C, D, S>
where
Self: Sized,
{
IonMobilityFrameGroupingIterator::new(self)
}
}
pub struct IonMobilityFrameIterator<
'lifespan,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D>,
R: IonMobilityFrameSource<C, D, S>,
> {
source: &'lifespan mut R,
frame_type: PhantomData<S>,
centroid_type: PhantomData<C>,
deconvoluted_type: PhantomData<D>,
index: usize,
back_index: usize,
}
impl<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D>,
R: IonMobilityFrameSource<C, D, S>,
> IonMobilityFrameIterator<'_, C, D, S, R>
{
pub fn new(source: &mut R) -> IonMobilityFrameIterator<'_, C, D, S, R> {
IonMobilityFrameIterator::<C, D, S, R> {
source,
index: 0,
back_index: 0,
frame_type: PhantomData,
centroid_type: PhantomData,
deconvoluted_type: PhantomData,
}
}
}
impl<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D>,
R: IonMobilityFrameSource<C, D, S>,
> Iterator for IonMobilityFrameIterator<'_, C, D, S, R>
{
type Item = S;
fn next(&mut self) -> Option<Self::Item> {
if self.index + self.back_index >= self.len() {
return None;
}
let result = self.source.get_frame_by_index(self.index);
self.index += 1;
result
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
self.index += n;
self.next()
}
}
impl<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D>,
R: IonMobilityFrameSource<C, D, S>,
> ExactSizeIterator for IonMobilityFrameIterator<'_, C, D, S, R>
{
fn len(&self) -> usize {
self.source.len()
}
}
impl<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D>,
R: IonMobilityFrameSource<C, D, S>,
> DoubleEndedIterator for IonMobilityFrameIterator<'_, C, D, S, R>
{
fn next_back(&mut self) -> Option<Self::Item> {
if self.index + self.back_index >= self.len() {
return None;
};
let i = self.len() - (self.back_index + 1);
let result = self.source.get_frame_by_index(i);
self.back_index += 1;
result
}
}
impl<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D>,
R: IonMobilityFrameSource<C, D, S>,
> IonMobilityFrameSource<C, D, S> for IonMobilityFrameIterator<'_, C, D, S, R>
{
fn reset(&mut self) {
self.index = 0;
self.back_index = 0;
}
fn get_frame_by_id(&mut self, id: &str) -> Option<S> {
self.source.get_frame_by_id(id)
}
fn get_frame_by_index(&mut self, index: usize) -> Option<S> {
self.source.get_frame_by_index(index)
}
fn get_frame_by_time(&mut self, time: f64) -> Option<S> {
self.source.get_frame_by_time(time)
}
fn get_index(&self) -> &OffsetIndex {
self.source.get_index()
}
fn set_index(&mut self, index: OffsetIndex) {
self.source.set_index(index);
}
fn detail_level(&self) -> &DetailLevel {
self.source.detail_level()
}
fn set_detail_level(&mut self, detail_level: DetailLevel) {
self.source.set_detail_level(detail_level);
}
}
impl<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D>,
R: IonMobilityFrameSource<C, D, S>,
> MSDataFileMetadata for IonMobilityFrameIterator<'_, C, D, S, R>
where
R: MSDataFileMetadata,
{
crate::delegate_impl_metadata_trait!(source);
}
#[derive(Debug)]
pub struct Generic3DIonMobilityFrameSource<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility> = Feature<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge = ChargedFeature<Mass, IonMobility>,
> {
source: R,
_cp: PhantomData<CP>,
_dp: PhantomData<DP>,
_c: PhantomData<C>,
_d: PhantomData<D>,
}
impl<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> MSDataFileMetadata for Generic3DIonMobilityFrameSource<CP, DP, R, C, D>
where
R: MSDataFileMetadata,
{
crate::delegate_impl_metadata_trait!(source);
}
impl<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> IonMobilityFrameSource<C, D, MultiLayerIonMobilityFrame<C, D>>
for Generic3DIonMobilityFrameSource<CP, DP, R, C, D>
{
fn detail_level(&self) -> &DetailLevel {
self.source.detail_level()
}
fn set_detail_level(&mut self, detail_level: DetailLevel) {
self.source.set_detail_level(detail_level);
}
fn reset(&mut self) {
self.source.reset()
}
fn get_frame_by_id(&mut self, id: &str) -> Option<MultiLayerIonMobilityFrame<C, D>> {
self.source.get_spectrum_by_id(id).and_then(|s| {
MultiLayerIonMobilityFrame::try_from(s).map_or_else(
|err| {
warn!("Failed to convert {id} to MultiLayerIonMobilityFrame: {err}");
None
},
Some,
)
})
}
fn get_frame_by_index(&mut self, index: usize) -> Option<MultiLayerIonMobilityFrame<C, D>> {
self.source.get_spectrum_by_index(index).and_then(|s| {
MultiLayerIonMobilityFrame::try_from(s).map_or_else(
|err| {
warn!("Failed to convert {index} to MultiLayerIonMobilityFrame: {err}");
None
},
Some,
)
})
}
fn get_frame_by_time(&mut self, time: f64) -> Option<MultiLayerIonMobilityFrame<C, D>> {
self.source.get_spectrum_by_time(time).and_then(|s| {
MultiLayerIonMobilityFrame::try_from(s).map_or_else(
|err| {
warn!("Failed to convert {time} to MultiLayerIonMobilityFrame: {err}");
None
},
Some,
)
})
}
fn get_index(&self) -> &OffsetIndex {
self.source.get_index()
}
fn set_index(&mut self, index: OffsetIndex) {
self.source.set_index(index)
}
}
impl<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> Iterator for Generic3DIonMobilityFrameSource<CP, DP, R, C, D>
{
type Item = MultiLayerIonMobilityFrame<C, D>;
fn next(&mut self) -> Option<Self::Item> {
self.source
.by_ref()
.filter_map(|s| MultiLayerIonMobilityFrame::try_from(s).ok())
.next()
}
}
impl<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> Generic3DIonMobilityFrameSource<CP, DP, R, C, D>
{
pub fn new(source: R) -> Self {
Self {
source,
_cp: PhantomData,
_dp: PhantomData,
_c: PhantomData,
_d: PhantomData,
}
}
pub fn get_inner(&self) -> &R {
&self.source
}
pub fn get_mut(&mut self) -> &mut R {
&mut self.source
}
pub fn into_inner(self) -> R {
self.source
}
}
#[derive(Debug, Error)]
pub enum IonMobilityFrameAccessError {
#[error("The requested frame was not found")]
FrameNotFound,
#[error("The requested frame native ID {0} was not found")]
FrameIdNotFound(String),
#[error("The requested frame index {0} was not found")]
FrameIndexNotFound(usize),
#[error("I/O error occurred while reading: {0:?}")]
IOError(#[source] Option<io::Error>),
}
impl From<IonMobilityFrameAccessError> for io::Error {
fn from(value: IonMobilityFrameAccessError) -> Self {
let s = value.to_string();
match value {
IonMobilityFrameAccessError::FrameNotFound => {
io::Error::new(io::ErrorKind::NotFound, s)
}
IonMobilityFrameAccessError::FrameIdNotFound(_) => {
io::Error::new(io::ErrorKind::NotFound, s)
}
IonMobilityFrameAccessError::FrameIndexNotFound(_) => {
io::Error::new(io::ErrorKind::NotFound, s)
}
IonMobilityFrameAccessError::IOError(e) => match e {
Some(e) => e,
None => io::Error::other(s),
},
}
}
}
impl From<IonMobilityFrameAccessError> for SpectrumAccessError {
fn from(value: IonMobilityFrameAccessError) -> Self {
match value {
IonMobilityFrameAccessError::FrameNotFound => SpectrumAccessError::SpectrumNotFound,
IonMobilityFrameAccessError::FrameIdNotFound(id) => {
SpectrumAccessError::SpectrumIdNotFound(id)
}
IonMobilityFrameAccessError::FrameIndexNotFound(i) => {
SpectrumAccessError::SpectrumIndexNotFound(i)
}
IonMobilityFrameAccessError::IOError(error) => SpectrumAccessError::IOError(error),
}
}
}
impl From<SpectrumAccessError> for IonMobilityFrameAccessError {
fn from(value: SpectrumAccessError) -> Self {
match value {
SpectrumAccessError::SpectrumNotFound => IonMobilityFrameAccessError::FrameNotFound,
SpectrumAccessError::SpectrumIdNotFound(id) => {
IonMobilityFrameAccessError::FrameIdNotFound(id)
}
SpectrumAccessError::SpectrumIndexNotFound(i) => {
IonMobilityFrameAccessError::FrameIndexNotFound(i)
}
SpectrumAccessError::IOError(error) => IonMobilityFrameAccessError::IOError(error),
}
}
}
pub trait RandomAccessIonMobilityFrameIterator<
C: FeatureLike<MZ, IonMobility> = Feature<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge = ChargedFeature<Mass, IonMobility>,
S: IonMobilityFrameLike<C, D> = MultiLayerIonMobilityFrame<C, D>,
>: IonMobilityFrameSource<C, D, S>
{
fn start_from_id(&mut self, id: &str) -> Result<&mut Self, IonMobilityFrameAccessError>;
fn start_from_index(&mut self, index: usize) -> Result<&mut Self, IonMobilityFrameAccessError>;
fn start_from_time(&mut self, time: f64) -> Result<&mut Self, IonMobilityFrameAccessError>;
}
impl<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> RandomAccessIonMobilityFrameIterator<C, D, MultiLayerIonMobilityFrame<C, D>>
for Generic3DIonMobilityFrameSource<CP, DP, R, C, D>
where
R: RandomAccessSpectrumIterator<CP, DP, MultiLayerSpectrum<CP, DP>>,
{
fn start_from_id(&mut self, id: &str) -> Result<&mut Self, IonMobilityFrameAccessError> {
match self.source.start_from_id(id) {
Ok(_) => Ok(self),
Err(e) => Err(IonMobilityFrameAccessError::from(e)),
}
}
fn start_from_index(&mut self, index: usize) -> Result<&mut Self, IonMobilityFrameAccessError> {
match self.source.start_from_index(index) {
Ok(_) => Ok(self),
Err(e) => Err(IonMobilityFrameAccessError::from(e)),
}
}
fn start_from_time(&mut self, time: f64) -> Result<&mut Self, IonMobilityFrameAccessError> {
match self.source.start_from_time(time) {
Ok(_) => Ok(self),
Err(e) => Err(IonMobilityFrameAccessError::from(e)),
}
}
}
impl<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D>,
R: IonMobilityFrameSource<C, D, S>,
> RandomAccessIonMobilityFrameIterator<C, D, S> for IonMobilityFrameIterator<'_, C, D, S, R>
{
fn start_from_id(&mut self, id: &str) -> Result<&mut Self, IonMobilityFrameAccessError> {
if let Some(scan) = self.get_frame_by_id(id) {
self.index = scan.index();
self.back_index = 0;
Ok(self)
} else if self.get_index().contains_key(id) {
Err(IonMobilityFrameAccessError::IOError(None))
} else {
Err(IonMobilityFrameAccessError::FrameIdNotFound(id.to_string()))
}
}
fn start_from_index(&mut self, index: usize) -> Result<&mut Self, IonMobilityFrameAccessError> {
if index < self.len() {
self.index = index;
self.back_index = 0;
Ok(self)
} else {
Err(IonMobilityFrameAccessError::FrameIndexNotFound(index))
}
}
fn start_from_time(&mut self, time: f64) -> Result<&mut Self, IonMobilityFrameAccessError> {
if let Some(scan) = self.get_frame_by_time(time) {
self.index = scan.index();
self.back_index = 0;
Ok(self)
} else if self
.get_frame_by_index(self.len() - 1)
.expect("Failed to fetch spectrum for boundary testing")
.start_time()
< time
{
Err(IonMobilityFrameAccessError::FrameNotFound)
} else {
Err(IonMobilityFrameAccessError::IOError(None))
}
}
}
pub trait IonMobilityFrameWriter<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
>
{
fn write_frame<S: IonMobilityFrameLike<C, D> + 'static>(
&mut self,
spectrum: &S,
) -> io::Result<usize>;
fn write_frame_owned<S: IonMobilityFrameLike<C, D> + 'static>(
&mut self,
spectrum: S,
) -> io::Result<usize> {
self.write_frame(&spectrum)
}
fn flush_frame(&mut self) -> io::Result<()>;
fn write_all_frames<'b, S: IonMobilityFrameLike<C, D> + 'static, T: Iterator<Item = &'b S>>(
&mut self,
iterator: T,
) -> io::Result<usize> {
let mut n = 0;
for spectrum in iterator {
n += self.write_frame(spectrum)?;
}
Ok(n)
}
fn write_all_frames_owned<S: IonMobilityFrameLike<C, D> + 'static, T: Iterator<Item = S>>(
&mut self,
iterator: T,
) -> io::Result<usize> {
let mut n = 0;
for spectrum in iterator {
n += self.write_frame_owned(spectrum)?;
}
Ok(n)
}
fn write_frame_group<
S: IonMobilityFrameLike<C, D> + 'static,
G: IonMobilityFrameGrouping<C, D, S> + 'static,
>(
&mut self,
group: &G,
) -> io::Result<usize> {
let mut n = 0;
if let Some(precursor) = group.precursor() {
n += self.write_frame(precursor)?;
}
for product in group.products() {
n += self.write_frame(product)?;
}
Ok(n)
}
fn write_frame_group_owned<
S: IonMobilityFrameLike<C, D> + 'static,
G: IonMobilityFrameGrouping<C, D, S> + 'static,
>(
&mut self,
group: G,
) -> io::Result<usize> {
let (precursor, products) = group.into_parts();
let mut n = 0;
if let Some(precursor) = precursor {
n += self.write_frame_owned(precursor)?;
}
for product in products {
n += self.write_frame_owned(product)?;
}
Ok(n)
}
fn write_all_frame_groups<
'b,
S: IonMobilityFrameLike<C, D> + 'static,
G: IonMobilityFrameGrouping<C, D, S> + 'static,
T: Iterator<Item = &'b G>,
>(
&mut self,
iterator: T,
) -> io::Result<usize> {
let mut n = 0;
for group in iterator {
n += self.write_frame_group(group)?;
}
Ok(n)
}
fn write_all_frame_groups_owned<
S: IonMobilityFrameLike<C, D> + 'static,
G: IonMobilityFrameGrouping<C, D, S> + 'static,
T: Iterator<Item = G>,
>(
&mut self,
iterator: T,
) -> io::Result<usize> {
let mut n = 0;
for group in iterator {
n += self.write_frame_group_owned(group)?;
}
Ok(n)
}
fn close_frames(&mut self) -> io::Result<()>;
}
#[derive(Debug)]
pub struct BorrowedGeneric3DIonMobilityFrameSource<
'a,
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility> = Feature<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge = ChargedFeature<Mass, IonMobility>,
> {
source: &'a mut R,
_cp: PhantomData<CP>,
_dp: PhantomData<DP>,
_c: PhantomData<C>,
_d: PhantomData<D>,
}
impl<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> MSDataFileMetadata for BorrowedGeneric3DIonMobilityFrameSource<'_, CP, DP, R, C, D>
where
R: MSDataFileMetadata,
{
crate::delegate_impl_metadata_trait!(source);
}
impl<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> IonMobilityFrameSource<C, D, MultiLayerIonMobilityFrame<C, D>>
for BorrowedGeneric3DIonMobilityFrameSource<'_, CP, DP, R, C, D>
{
fn detail_level(&self) -> &DetailLevel {
self.source.detail_level()
}
fn set_detail_level(&mut self, detail_level: DetailLevel) {
self.source.set_detail_level(detail_level);
}
fn reset(&mut self) {
self.source.reset()
}
fn get_frame_by_id(&mut self, id: &str) -> Option<MultiLayerIonMobilityFrame<C, D>> {
self.source.get_spectrum_by_id(id).and_then(|s| {
MultiLayerIonMobilityFrame::try_from(s).map_or_else(
|err| {
warn!("Failed to convert {id} to MultiLayerIonMobilityFrame: {err}");
None
},
Some,
)
})
}
fn get_frame_by_index(&mut self, index: usize) -> Option<MultiLayerIonMobilityFrame<C, D>> {
self.source.get_spectrum_by_index(index).and_then(|s| {
MultiLayerIonMobilityFrame::try_from(s).map_or_else(
|err| {
warn!("Failed to convert {index} to MultiLayerIonMobilityFrame: {err}");
None
},
Some,
)
})
}
fn get_frame_by_time(&mut self, time: f64) -> Option<MultiLayerIonMobilityFrame<C, D>> {
self.source.get_spectrum_by_time(time).and_then(|s| {
MultiLayerIonMobilityFrame::try_from(s).map_or_else(
|err| {
warn!("Failed to convert {time} to MultiLayerIonMobilityFrame: {err}");
None
},
Some,
)
})
}
fn get_index(&self) -> &OffsetIndex {
self.source.get_index()
}
fn set_index(&mut self, index: OffsetIndex) {
self.source.set_index(index)
}
}
impl<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> Iterator for BorrowedGeneric3DIonMobilityFrameSource<'_, CP, DP, R, C, D>
{
type Item = MultiLayerIonMobilityFrame<C, D>;
fn next(&mut self) -> Option<Self::Item> {
self.source
.by_ref()
.filter_map(|s| MultiLayerIonMobilityFrame::try_from(s).ok())
.next()
}
}
impl<
'a,
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> BorrowedGeneric3DIonMobilityFrameSource<'a, CP, DP, R, C, D>
{
#[allow(unused)]
pub fn new(source: &'a mut R) -> Self {
Self {
source,
_cp: PhantomData,
_dp: PhantomData,
_c: PhantomData,
_d: PhantomData,
}
}
}
impl<
CP: CentroidLike,
DP: DeconvolutedCentroidLike,
R: SpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
> RandomAccessIonMobilityFrameIterator<C, D, MultiLayerIonMobilityFrame<C, D>>
for BorrowedGeneric3DIonMobilityFrameSource<'_, CP, DP, R, C, D>
where
R: RandomAccessSpectrumIterator<CP, DP, MultiLayerSpectrum<CP, DP>>,
{
fn start_from_id(&mut self, id: &str) -> Result<&mut Self, IonMobilityFrameAccessError> {
match self.source.start_from_id(id) {
Ok(_) => Ok(self),
Err(e) => Err(match e {
SpectrumAccessError::SpectrumIdNotFound(id) => {
IonMobilityFrameAccessError::FrameIdNotFound(id)
}
SpectrumAccessError::SpectrumNotFound => IonMobilityFrameAccessError::FrameNotFound,
SpectrumAccessError::IOError(e) => IonMobilityFrameAccessError::IOError(e),
_ => todo!(),
}),
}
}
fn start_from_index(&mut self, index: usize) -> Result<&mut Self, IonMobilityFrameAccessError> {
match self.source.start_from_index(index) {
Ok(_) => Ok(self),
Err(e) => Err(match e {
SpectrumAccessError::SpectrumIndexNotFound(i) => {
IonMobilityFrameAccessError::FrameIndexNotFound(i)
}
SpectrumAccessError::SpectrumNotFound => IonMobilityFrameAccessError::FrameNotFound,
SpectrumAccessError::IOError(e) => IonMobilityFrameAccessError::IOError(e),
_ => todo!(),
}),
}
}
fn start_from_time(&mut self, time: f64) -> Result<&mut Self, IonMobilityFrameAccessError> {
match self.source.start_from_time(time) {
Ok(_) => Ok(self),
Err(e) => Err(match e {
SpectrumAccessError::SpectrumNotFound => IonMobilityFrameAccessError::FrameNotFound,
SpectrumAccessError::IOError(e) => IonMobilityFrameAccessError::IOError(e),
_ => todo!(),
}),
}
}
}
pub trait RandomAccessIonMobilityFrameGroupingIterator<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D> = MultiLayerIonMobilityFrame<C, D>,
G: IonMobilityFrameGrouping<C, D, S> = IonMobilityFrameGroup<
C,
D,
MultiLayerIonMobilityFrame<C, D>,
>,
>: Iterator<Item = G>
{
fn start_from_id(&mut self, id: &str) -> Result<&mut Self, IonMobilityFrameAccessError>;
fn start_from_index(&mut self, index: usize) -> Result<&mut Self, IonMobilityFrameAccessError>;
fn start_from_time(&mut self, time: f64) -> Result<&mut Self, IonMobilityFrameAccessError>;
fn reset_state(&mut self);
}
#[derive(Debug, Error)]
pub enum IntoIonMobilityFrameSourceError {
#[error("No ion mobility frames were found")]
NoIonMobilityFramesFound,
#[error("Cannot convert to requested frame type")]
ConversionNotPossible,
}
pub trait IntoIonMobilityFrameSource<C: CentroidLike, D: DeconvolutedCentroidLike>:
SpectrumSource<C, D, MultiLayerSpectrum<C, D>> + Sized
{
type IonMobilityFrameSource<CF: FeatureLike<MZ, IonMobility>, DF: FeatureLike<Mass, IonMobility> + KnownCharge>: IonMobilityFrameSource<CF, DF, MultiLayerIonMobilityFrame<CF, DF>>;
fn try_into_frame_source<
CF: FeatureLike<MZ, IonMobility>,
DF: FeatureLike<Mass, IonMobility> + KnownCharge,
>(
self,
) -> Result<Self::IonMobilityFrameSource<CF, DF>, IntoIonMobilityFrameSourceError>;
fn into_frame_source<
CF: FeatureLike<MZ, IonMobility>,
DF: FeatureLike<Mass, IonMobility> + KnownCharge,
>(
self,
) -> Self::IonMobilityFrameSource<CF, DF> {
self.try_into_frame_source().unwrap()
}
fn has_ion_mobility(&mut self) -> Option<HasIonMobility> {
let details = *self.detail_level();
self.set_detail_level(DetailLevel::Lazy);
let n = self.len();
if n == 0 {
self.set_detail_level(details);
return Some(HasIonMobility::None);
}
let mut handle = self.iter();
let mut status = HasIonMobility::None;
let step_size = if n > 100 { n / 100 } else { n };
for i in (0..n).step_by(step_size) {
let spec = handle.get_spectrum_by_index(i)?;
let cls = spec.has_ion_mobility_class();
status = status.max(cls);
if status > HasIonMobility::None {
break;
}
}
self.set_detail_level(details);
Some(status)
}
}
#[cfg(feature = "async_partial")]
mod async_traits {
use std::future::Future;
use futures::{
stream::{self, FusedStream},
Stream,
};
use super::*;
use crate::io::traits::{AsyncRandomAccessSpectrumIterator, AsyncSpectrumSource};
pub trait AsyncIonMobilityFrameSource<
C: FeatureLike<MZ, IonMobility> = Feature<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge = ChargedFeature<Mass, IonMobility>,
S: IonMobilityFrameLike<C, D> = MultiLayerIonMobilityFrame<C, D>,
>: Send
{
fn reset(&mut self) -> impl Future<Output = ()>;
fn detail_level(&self) -> &DetailLevel;
fn set_detail_level(&mut self, detail_level: DetailLevel);
fn get_frame_by_id(&mut self, id: &str) -> impl Future<Output = Option<S>>;
fn get_frame_by_index(&mut self, index: usize) -> impl Future<Output = Option<S>>;
#[allow(async_fn_in_trait)]
async fn get_frame_by_time(&mut self, time: f64) -> Option<S> {
{
let n = self.len();
if n == 0 && !self.get_index().init {
warn!("Attempting to use `get_frame_by_time` when the frame index has not been initialized.");
return None;
}
let mut lo: usize = 0;
let mut hi: usize = n;
let mut best_error: f64 = f64::INFINITY;
let mut best_match: Option<S> = None;
if lo == hi {
return None;
}
let original_detail_level = *self.detail_level();
self.set_detail_level(DetailLevel::MetadataOnly);
while hi != lo {
let mid = (hi + lo) / 2;
let scan = self.get_frame_by_index(mid).await?;
let scan_time = scan.start_time();
let err = (scan_time - time).abs();
if err < best_error {
best_error = err;
best_match = Some(scan);
}
if hi.saturating_sub(1) == lo {
self.set_detail_level(original_detail_level);
return best_match;
} else if scan_time > time {
hi = mid;
} else {
lo = mid;
}
}
self.set_detail_level(original_detail_level);
best_match
}
}
fn len(&self) -> usize {
self.get_index().len()
}
fn is_empty(&self) -> bool {
self.len() == 0
}
fn get_index(&self) -> &OffsetIndex;
fn set_index(&mut self, index: OffsetIndex);
fn _offset_of_id(&self, id: &str) -> Option<u64> {
self.get_index().get(id)
}
fn _offset_of_index(&self, index: usize) -> Option<u64> {
self.get_index()
.get_index(index)
.map(|(_id, offset)| offset)
}
#[allow(async_fn_in_trait)]
async fn _offset_of_time(&mut self, time: f64) -> Option<u64> {
{
match self.get_frame_by_time(time).await {
Some(scan) => self._offset_of_index(scan.index()),
None => None,
}
}
}
fn read_next_frame(&mut self) -> impl Future<Output = Option<S>>;
fn as_stream(&mut self) -> impl IonMobilityFrameStream<C, D, S> + Unpin + '_ {
Box::pin(stream::unfold(self, |reader| async {
let frame = reader.read_next_frame();
frame.await.map(|val| (val, reader))
}))
}
}
pub trait AsyncRandomAccessIonMobilityFrameIterator<
C: FeatureLike<MZ, IonMobility> = Feature<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge = ChargedFeature<Mass, IonMobility>,
S: IonMobilityFrameLike<C, D> = MultiLayerIonMobilityFrame<C, D>,
>: AsyncIonMobilityFrameSource<C, D, S> + Sized
{
fn start_from_id(
&mut self,
id: &str,
) -> impl Future<Output = Result<&mut Self, IonMobilityFrameAccessError>>;
fn start_from_index(
&mut self,
index: usize,
) -> impl Future<Output = Result<&mut Self, IonMobilityFrameAccessError>>;
fn start_from_time(
&mut self,
time: f64,
) -> impl Future<Output = Result<&mut Self, IonMobilityFrameAccessError>>;
}
pub trait IonMobilityFrameStream<
C: FeatureLike<MZ, IonMobility> = Feature<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge = ChargedFeature<Mass, IonMobility>,
S: IonMobilityFrameLike<C, D> = MultiLayerIonMobilityFrame<C, D>,
>: Stream<Item = S> + FusedStream
{
}
impl<
C: FeatureLike<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge,
S: IonMobilityFrameLike<C, D>,
T: Stream<Item = S> + FusedStream,
> IonMobilityFrameStream<C, D, S> for T
{
}
#[derive(Debug)]
pub struct AsyncGeneric3DIonMobilityFrameSource<
CP: CentroidLike + Send + Sync,
DP: DeconvolutedCentroidLike + Send + Sync,
R: AsyncSpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility> + Send + Sync = Feature<MZ, IonMobility>,
D: FeatureLike<Mass, IonMobility> + KnownCharge + Send + Sync = ChargedFeature<
Mass,
IonMobility,
>,
> {
source: R,
_cp: PhantomData<CP>,
_dp: PhantomData<DP>,
_c: PhantomData<C>,
_d: PhantomData<D>,
}
impl<
CP: CentroidLike + Send + Sync,
DP: DeconvolutedCentroidLike + Send + Sync,
R: AsyncSpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility> + Send + Sync,
D: FeatureLike<Mass, IonMobility> + KnownCharge + Send + Sync,
> AsyncGeneric3DIonMobilityFrameSource<CP, DP, R, C, D>
{
pub fn new(source: R) -> Self {
Self {
source,
_cp: PhantomData,
_dp: PhantomData,
_c: PhantomData,
_d: PhantomData,
}
}
pub fn get_inner(&self) -> &R {
&self.source
}
pub fn get_mut(&mut self) -> &mut R {
&mut self.source
}
pub fn into_inner(self) -> R {
self.source
}
}
impl<
CP: CentroidLike + Send + Sync,
DP: DeconvolutedCentroidLike + Send + Sync,
R: AsyncSpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility> + Send + Sync,
D: FeatureLike<Mass, IonMobility> + KnownCharge + Send + Sync,
> MSDataFileMetadata for AsyncGeneric3DIonMobilityFrameSource<CP, DP, R, C, D>
where
R: MSDataFileMetadata,
{
crate::delegate_impl_metadata_trait!(source);
}
impl<
CP: CentroidLike + Send + Sync,
DP: DeconvolutedCentroidLike + Send + Sync,
R: AsyncSpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility> + Send + Sync,
D: FeatureLike<Mass, IonMobility> + KnownCharge + Send + Sync,
> AsyncIonMobilityFrameSource<C, D, MultiLayerIonMobilityFrame<C, D>>
for AsyncGeneric3DIonMobilityFrameSource<CP, DP, R, C, D>
{
fn detail_level(&self) -> &DetailLevel {
self.source.detail_level()
}
fn set_detail_level(&mut self, detail_level: DetailLevel) {
self.source.set_detail_level(detail_level);
}
async fn reset(&mut self) {
self.source.reset().await
}
async fn get_frame_by_id(&mut self, id: &str) -> Option<MultiLayerIonMobilityFrame<C, D>> {
let s = self.source.get_spectrum_by_id(id).await?;
MultiLayerIonMobilityFrame::try_from(s).map_or_else(
|err| {
warn!("Failed to convert {id} to MultiLayerIonMobilityFrame: {err}");
None
},
Some,
)
}
async fn get_frame_by_index(
&mut self,
index: usize,
) -> Option<MultiLayerIonMobilityFrame<C, D>> {
let s = self.source.get_spectrum_by_index(index).await?;
MultiLayerIonMobilityFrame::try_from(s).map_or_else(
|err| {
warn!("Failed to convert {index} to MultiLayerIonMobilityFrame: {err}");
None
},
Some,
)
}
async fn get_frame_by_time(
&mut self,
time: f64,
) -> Option<MultiLayerIonMobilityFrame<C, D>> {
let s = self.source.get_spectrum_by_time(time).await?;
MultiLayerIonMobilityFrame::try_from(s).map_or_else(
|err| {
warn!("Failed to convert {time} to MultiLayerIonMobilityFrame: {err}");
None
},
Some,
)
}
async fn read_next_frame(&mut self) -> Option<MultiLayerIonMobilityFrame<C, D>> {
while let Some(s) = self.source.read_next().await {
match MultiLayerIonMobilityFrame::try_from(s) {
Ok(frame) => return Some(frame),
Err(err) => {
warn!("Failed to convert spectrum to MultiLayerIonMobilityFrame: {err}")
}
}
}
None
}
fn get_index(&self) -> &OffsetIndex {
self.source.get_index()
}
fn set_index(&mut self, index: OffsetIndex) {
self.source.set_index(index)
}
}
impl<
CP: CentroidLike + Send + Sync,
DP: DeconvolutedCentroidLike + Send + Sync,
R: AsyncSpectrumSource<CP, DP, MultiLayerSpectrum<CP, DP>>,
C: FeatureLike<MZ, IonMobility> + Send + Sync,
D: FeatureLike<Mass, IonMobility> + KnownCharge + Send + Sync,
> AsyncRandomAccessIonMobilityFrameIterator<C, D, MultiLayerIonMobilityFrame<C, D>>
for AsyncGeneric3DIonMobilityFrameSource<CP, DP, R, C, D>
where
R: AsyncRandomAccessSpectrumIterator<CP, DP, MultiLayerSpectrum<CP, DP>>,
{
async fn start_from_id(
&mut self,
id: &str,
) -> Result<&mut Self, IonMobilityFrameAccessError> {
match self.source.start_from_id(id).await {
Ok(_) => Ok(self),
Err(e) => Err(IonMobilityFrameAccessError::from(e)),
}
}
async fn start_from_index(
&mut self,
index: usize,
) -> Result<&mut Self, IonMobilityFrameAccessError> {
match self.source.start_from_index(index).await {
Ok(_) => Ok(self),
Err(e) => Err(IonMobilityFrameAccessError::from(e)),
}
}
async fn start_from_time(
&mut self,
time: f64,
) -> Result<&mut Self, IonMobilityFrameAccessError> {
match self.source.start_from_time(time).await {
Ok(_) => Ok(self),
Err(e) => Err(IonMobilityFrameAccessError::from(e)),
}
}
}
pub trait AsyncIntoIonMobilityFrameSource<
C: CentroidLike + Send + Sync,
D: DeconvolutedCentroidLike + Send + Sync,
>: AsyncSpectrumSource<C, D, MultiLayerSpectrum<C, D>> + Sized
{
type IonMobilityFrameSource<
CF: FeatureLike<MZ, IonMobility> + Send + Sync,
DF: FeatureLike<Mass, IonMobility> + KnownCharge + Send + Sync,
>: AsyncIonMobilityFrameSource<CF, DF, MultiLayerIonMobilityFrame<CF, DF>>;
fn try_into_frame_source<
CF: FeatureLike<MZ, IonMobility> + Send + Sync,
DF: FeatureLike<Mass, IonMobility> + KnownCharge + Send + Sync,
>(
self,
) -> impl Future<
Output = Result<Self::IonMobilityFrameSource<CF, DF>, IntoIonMobilityFrameSourceError>,
>;
#[allow(async_fn_in_trait)]
async fn into_frame_source<
CF: FeatureLike<MZ, IonMobility> + Send + Sync,
DF: FeatureLike<Mass, IonMobility> + KnownCharge + Send + Sync,
>(
self,
) -> Self::IonMobilityFrameSource<CF, DF> {
self.try_into_frame_source().await.unwrap()
}
#[allow(async_fn_in_trait)]
async fn has_ion_mobility(&mut self) -> Option<HasIonMobility> {
let details = *self.detail_level();
self.set_detail_level(DetailLevel::Lazy);
let n = self.len();
if n == 0 {
self.set_detail_level(details);
return Some(HasIonMobility::None);
}
let mut status = HasIonMobility::None;
let step_size = if n > 100 { n / 100 } else { n };
for i in (0..n).step_by(step_size) {
let spec = self.get_spectrum_by_index(i).await?;
let cls = spec.has_ion_mobility_class();
status = status.max(cls);
if status > HasIonMobility::None {
break;
}
}
self.set_detail_level(details);
Some(status)
}
}
}
#[cfg(feature = "async_partial")]
pub use async_traits::{
AsyncGeneric3DIonMobilityFrameSource, AsyncIntoIonMobilityFrameSource,
AsyncIonMobilityFrameSource, AsyncRandomAccessIonMobilityFrameIterator, IonMobilityFrameStream,
};
#[cfg(all(test, feature = "async_partial"))]
mod async_trait_tests {
use super::*;
use crate::io::traits::{AsyncRandomAccessSpectrumIterator, AsyncSpectrumSource};
use futures::StreamExt;
use mzpeaks::{CentroidPeak, DeconvolutedPeak};
#[allow(unused)]
async fn provided_methods_are_callable<S: AsyncIonMobilityFrameSource>(src: &mut S) {
let _ = src.get_frame_by_time(1.0).await;
let _ = src._offset_of_time(1.0).await;
let _ = src.len();
let mut stream = src.as_stream();
let _first = stream.next().await;
}
#[allow(unused)]
async fn random_access_methods_are_callable<S: AsyncRandomAccessIonMobilityFrameIterator>(
src: &mut S,
) {
let _ = src.start_from_id("x").await;
let _ = src.start_from_index(0).await;
let _ = src.start_from_time(0.0).await;
}
#[allow(unused)]
async fn adapter_satisfies_frame_source<R>(source: R)
where
R: AsyncSpectrumSource<
CentroidPeak,
DeconvolutedPeak,
MultiLayerSpectrum<CentroidPeak, DeconvolutedPeak>,
>,
{
fn assert_frame_source<T: AsyncIonMobilityFrameSource>(_: &T) {}
let mut adapter =
AsyncGeneric3DIonMobilityFrameSource::<CentroidPeak, DeconvolutedPeak, R>::new(source);
assert_frame_source(&adapter);
let _ = adapter.get_frame_by_id("x").await;
let _ = adapter.get_frame_by_index(0).await;
let _ = adapter.get_frame_by_time(0.0).await;
let _ = adapter.read_next_frame().await;
adapter.reset().await;
let mut stream = adapter.as_stream();
let _first = stream.next().await;
}
#[allow(unused)]
async fn adapter_satisfies_random_access<R>(source: R)
where
R: AsyncRandomAccessSpectrumIterator<
CentroidPeak,
DeconvolutedPeak,
MultiLayerSpectrum<CentroidPeak, DeconvolutedPeak>,
>,
{
fn assert_random_access<T: AsyncRandomAccessIonMobilityFrameIterator>(_: &T) {}
let mut adapter =
AsyncGeneric3DIonMobilityFrameSource::<CentroidPeak, DeconvolutedPeak, R>::new(source);
assert_random_access(&adapter);
let _ = adapter.start_from_id("x").await;
let _ = adapter.start_from_index(0).await;
let _ = adapter.start_from_time(0.0).await;
}
}