1use std::{marker::PhantomData, mem};
2
3use mzpeaks::{
4 coordinate::{IonMobility, Mass, MZ},
5 feature::FeatureLike,
6 KnownCharge,
7};
8
9use crate::frame::{IonMobilityFrameLike, MultiLayerIonMobilityFrame};
10
11use super::utils::GroupIterState;
12
13pub trait IonMobilityFrameGrouping<
15 C: FeatureLike<MZ, IonMobility>,
16 D: FeatureLike<Mass, IonMobility> + KnownCharge,
17 S: IonMobilityFrameLike<C, D> = MultiLayerIonMobilityFrame<C, D>,
18>: Default
19{
20 fn precursor(&self) -> Option<&S>;
22 fn precursor_mut(&mut self) -> Option<&mut S>;
24 fn set_precursor(&mut self, prec: S);
26
27 fn products(&self) -> &[S];
29
30 fn products_mut(&mut self) -> &mut Vec<S>;
32
33 fn total_frames(&self) -> usize {
35 self.precursor().is_some() as usize + self.products().len()
36 }
37
38 fn earliest_frame(&self) -> Option<&S> {
40 self.precursor().or_else(|| {
41 self.products().iter().min_by(|a, b| {
42 a.acquisition()
43 .start_time()
44 .total_cmp(&b.acquisition().start_time())
45 })
46 })
47 }
48
49 fn latest_frame(&self) -> Option<&S> {
51 let product = self.products().iter().max_by(|a, b| {
52 a.acquisition()
53 .start_time()
54 .total_cmp(&b.acquisition().start_time())
55 });
56 match (self.precursor(), product) {
57 (None, None) => None,
58 (None, Some(c)) => Some(c),
59 (Some(p), None) => Some(p),
60 (Some(p), Some(c)) => {
61 if p.start_time() > c.start_time() {
62 Some(p)
63 } else {
64 Some(c)
65 }
66 }
67 }
68 }
69
70 fn lowest_ms_level(&self) -> Option<u8> {
72 let prec_level = self.precursor().map(|p| p.ms_level()).unwrap_or(u8::MAX);
73 let val = self
74 .products()
75 .iter()
76 .fold(prec_level, |state, s| state.min(s.ms_level()));
77 if val > 0 {
78 Some(val)
79 } else {
80 None
81 }
82 }
83
84 fn highest_ms_level(&self) -> Option<u8> {
86 let prec_level = self
87 .precursor()
88 .map(|p| p.ms_level())
89 .unwrap_or_else(|| u8::MIN);
90 let val = self
91 .products()
92 .iter()
93 .fold(prec_level, |state, s| state.max(s.ms_level()));
94 if val > 0 {
95 Some(val)
96 } else {
97 None
98 }
99 }
100
101 fn into_parts(self) -> (Option<S>, Vec<S>);
103}
104
105#[derive(Debug, Clone, PartialEq)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110pub struct IonMobilityFrameGroup<C, D, S>
111where
112 C: FeatureLike<MZ, IonMobility>,
113 D: FeatureLike<Mass, IonMobility> + KnownCharge,
114 S: IonMobilityFrameLike<C, D>,
115{
116 pub precursor: Option<S>,
118 pub products: Vec<S>,
121 centroid_type: PhantomData<C>,
122 deconvoluted_type: PhantomData<D>,
123}
124
125impl<C, D, S> Default for IonMobilityFrameGroup<C, D, S>
126where
127 C: FeatureLike<MZ, IonMobility>,
128 D: FeatureLike<Mass, IonMobility> + KnownCharge,
129 S: IonMobilityFrameLike<C, D>,
130{
131 fn default() -> Self {
132 Self {
133 precursor: None,
134 products: Vec::new(),
135 centroid_type: Default::default(),
136 deconvoluted_type: Default::default(),
137 }
138 }
139}
140
141impl<C, D, S> IonMobilityFrameGrouping<C, D, S> for IonMobilityFrameGroup<C, D, S>
142where
143 C: FeatureLike<MZ, IonMobility>,
144 D: FeatureLike<Mass, IonMobility> + KnownCharge,
145 S: IonMobilityFrameLike<C, D>,
146{
147 fn precursor(&self) -> Option<&S> {
148 match &self.precursor {
149 Some(prec) => Some(prec),
150 None => None,
151 }
152 }
153
154 fn precursor_mut(&mut self) -> Option<&mut S> {
155 match &mut self.precursor {
156 Some(prec) => Some(prec),
157 None => None,
158 }
159 }
160
161 fn set_precursor(&mut self, prec: S) {
162 self.precursor = Some(prec)
163 }
164
165 fn products(&self) -> &[S] {
166 &self.products
167 }
168
169 fn products_mut(&mut self) -> &mut Vec<S> {
170 &mut self.products
171 }
172
173 fn into_parts(self) -> (Option<S>, Vec<S>) {
174 (self.precursor, self.products)
175 }
176}
177
178pub struct IonMobilityFrameGroupIntoIter<
179 C: FeatureLike<MZ, IonMobility>,
180 D: FeatureLike<Mass, IonMobility> + KnownCharge,
181 S: IonMobilityFrameLike<C, D> + Default,
182 G: IonMobilityFrameGrouping<C, D, S>,
183> {
184 group: G,
185 state: GroupIterState,
186 _c: PhantomData<C>,
187 _d: PhantomData<D>,
188 _s: PhantomData<S>,
189}
190
191impl<
192 C: FeatureLike<MZ, IonMobility>,
193 D: FeatureLike<Mass, IonMobility> + KnownCharge,
194 S: IonMobilityFrameLike<C, D> + Default,
195 G: IonMobilityFrameGrouping<C, D, S>,
196 > Iterator for IonMobilityFrameGroupIntoIter<C, D, S, G>
197{
198 type Item = S;
199
200 fn next(&mut self) -> Option<Self::Item> {
201 {
202 let n = self.n_products();
203 let emission = match self.state {
204 GroupIterState::Precursor => match self.group.precursor_mut() {
205 Some(prec) => {
206 if n > 0 {
207 self.state = GroupIterState::Product(0);
208 } else {
209 self.state = GroupIterState::Done;
210 }
211 Some(mem::take(prec))
212 }
213 None => {
214 if n > 0 {
215 self.state = if n > 1 {
216 GroupIterState::Product(1)
217 } else {
218 GroupIterState::Done
219 };
220 Some(mem::take(&mut self.group.products_mut()[0]))
221 } else {
222 self.state = GroupIterState::Done;
223 None
224 }
225 }
226 },
227 GroupIterState::Product(i) => {
228 if i < n.saturating_sub(1) {
229 self.state = GroupIterState::Product(i + 1);
230 Some(mem::take(&mut self.group.products_mut()[i]))
231 } else {
232 self.state = GroupIterState::Done;
233 Some(mem::take(&mut self.group.products_mut()[i]))
234 }
235 }
236 GroupIterState::Done => None,
237 };
238 emission
239 }
240 }
241}
242
243impl<
244 C: FeatureLike<MZ, IonMobility>,
245 D: FeatureLike<Mass, IonMobility> + KnownCharge,
246 S: IonMobilityFrameLike<C, D> + Default,
247 G: IonMobilityFrameGrouping<C, D, S>,
248 > IonMobilityFrameGroupIntoIter<C, D, S, G>
249{
250 pub fn new(group: G) -> Self {
251 Self {
252 group,
253 state: GroupIterState::Precursor,
254 _c: PhantomData,
255 _d: PhantomData,
256 _s: PhantomData,
257 }
258 }
259
260 fn n_products(&self) -> usize {
261 self.group.products().len()
262 }
263}
264
265pub struct IonMobilityFrameGroupIter<
268 'a,
269 C: FeatureLike<MZ, IonMobility>,
270 D: FeatureLike<Mass, IonMobility> + KnownCharge,
271 S: IonMobilityFrameLike<C, D> + 'a + Default,
272 G: IonMobilityFrameGrouping<C, D, S>,
273> {
274 group: &'a G,
275 state: GroupIterState,
276 _c: PhantomData<C>,
277 _d: PhantomData<D>,
278 _s: PhantomData<S>,
279}
280
281impl<
282 'a,
283 C: FeatureLike<MZ, IonMobility>,
284 D: FeatureLike<Mass, IonMobility> + KnownCharge,
285 S: IonMobilityFrameLike<C, D> + 'a + Default,
286 G: IonMobilityFrameGrouping<C, D, S>,
287 > Iterator for IonMobilityFrameGroupIter<'a, C, D, S, G>
288{
289 type Item = &'a S;
290
291 fn next(&mut self) -> Option<Self::Item> {
292 {
293 let n = self.n_products();
294 let emission = match self.state {
295 GroupIterState::Precursor => match self.group.precursor() {
296 Some(prec) => {
297 if n > 0 {
298 self.state = GroupIterState::Product(0);
299 } else {
300 self.state = GroupIterState::Done;
301 }
302 Some(prec)
303 }
304 None => {
305 if n > 0 {
306 self.state = if n > 1 {
307 GroupIterState::Product(1)
308 } else {
309 GroupIterState::Done
310 };
311 Some(&self.group.products()[0])
312 } else {
313 self.state = GroupIterState::Done;
314 None
315 }
316 }
317 },
318 GroupIterState::Product(i) => {
319 if i < n.saturating_sub(1) {
320 self.state = GroupIterState::Product(i + 1);
321 Some(&self.group.products()[i])
322 } else {
323 self.state = GroupIterState::Done;
324 Some(&self.group.products()[i])
325 }
326 }
327 GroupIterState::Done => None,
328 };
329 emission
330 }
331 }
332}
333
334impl<
335 'a,
336 C: FeatureLike<MZ, IonMobility>,
337 D: FeatureLike<Mass, IonMobility> + KnownCharge,
338 S: IonMobilityFrameLike<C, D> + Default,
339 G: IonMobilityFrameGrouping<C, D, S>,
340 > IonMobilityFrameGroupIter<'a, C, D, S, G>
341{
342 pub fn new(group: &'a G) -> Self {
343 Self {
344 group,
345 state: GroupIterState::Precursor,
346 _c: PhantomData,
347 _d: PhantomData,
348 _s: PhantomData,
349 }
350 }
351
352 fn n_products(&self) -> usize {
353 self.group.products().len()
354 }
355}
356
357impl<C, D, S> IntoIterator for IonMobilityFrameGroup<C, D, S>
360where
361 C: FeatureLike<MZ, IonMobility>,
362 D: FeatureLike<Mass, IonMobility> + KnownCharge,
363 S: IonMobilityFrameLike<C, D> + Default,
364{
365 type Item = S;
366
367 type IntoIter = IonMobilityFrameGroupIntoIter<C, D, S, Self>;
368
369 fn into_iter(self) -> Self::IntoIter {
370 IonMobilityFrameGroupIntoIter::new(self)
371 }
372}
373
374impl<'a, C, D, S> IonMobilityFrameGroup<C, D, S>
375where
376 C: FeatureLike<MZ, IonMobility>,
377 D: FeatureLike<Mass, IonMobility> + KnownCharge,
378 S: IonMobilityFrameLike<C, D> + Default,
379{
380 pub fn new(precursor: Option<S>, products: Vec<S>) -> Self {
381 Self {
382 precursor,
383 products,
384 centroid_type: PhantomData,
385 deconvoluted_type: PhantomData,
386 }
387 }
388
389 pub fn iter(&'a self) -> IonMobilityFrameGroupIter<'a, C, D, S, Self> {
392 IonMobilityFrameGroupIter::new(self)
393 }
394}
395
396#[cfg(test)]
397mod test {
398 use mzpeaks::feature::{ChargedFeature, Feature};
399
400 use super::*;
401
402 fn make_group() -> IonMobilityFrameGroup<
403 Feature<MZ, IonMobility>,
404 ChargedFeature<Mass, IonMobility>,
405 MultiLayerIonMobilityFrame,
406 > {
407 let mut spec1 = MultiLayerIonMobilityFrame::default();
408 {
409 let desc1 = spec1.description_mut();
410 desc1.id = "index=0".into();
411 desc1.ms_level = 1;
412 desc1.index = 0;
413 desc1.acquisition.first_scan_mut().unwrap().start_time = 100.0;
414 }
415
416 let mut spec2 = MultiLayerIonMobilityFrame::default();
417 {
418 let desc2 = spec2.description_mut();
419 desc2.id = "index=1".into();
420 desc2.ms_level = 2;
421 desc2.index = 1;
422 desc2.acquisition.first_scan_mut().unwrap().start_time = 101.0;
423 }
424
425 let mut spec3 = MultiLayerIonMobilityFrame::default();
426 {
427 let desc3 = spec3.description_mut();
428 desc3.id = "index=2".into();
429 desc3.ms_level = 2;
430 desc3.index = 2;
431 desc3.acquisition.first_scan_mut().unwrap().start_time = 101.5;
432 }
433
434 IonMobilityFrameGroup::new(Some(spec1), vec![spec2, spec3])
435 }
436
437 #[test]
438 fn test_construct() {
439 let group = make_group();
440 assert_eq!(group.lowest_ms_level().unwrap(), 1);
441 assert_eq!(group.highest_ms_level().unwrap(), 2);
442
443 assert_eq!(group.earliest_frame().unwrap().ms_level(), 1);
444 assert_eq!(group.latest_frame().unwrap().ms_level(), 2);
445
446 assert_eq!(group.iter().count(), 3);
447 assert_eq!(group.into_iter().count(), 3);
448 }
449}