1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
//! The `SpectrumSource` trait: every vendor parser implements this.
use crateResult;
use crate;
/// Boxed, fallible spectrum iterator returned by
/// [`SpectrumSource::iter_spectra`].
pub type SpectrumIter<'a> = ;
/// Boxed, fallible chromatogram iterator returned by
/// [`SpectrumSource::iter_chromatograms`].
pub type ChromatogramIter<'a> = ;
/// A source of decoded mass spectra.
///
/// Vendors implement this on whatever value carries their open file state
/// (e.g. `RawFileReader` + a `&mut Read+Seek` source for opentfraw, a
/// `Reader` for opentimstdf).
///
/// The trait uses boxed iterators rather than RPITIT so that implementations
/// can pick a different underlying iterator type per call without leaking
/// that into the trait signature, and so consumers can hold a
/// `&mut dyn SpectrumSource` for downstream plumbing (mzML writer, ingest
/// pipelines, language bindings).
///
/// # Errors
///
/// Iterators yield `Result` items with the stack-wide
/// [`Error`](crate::Error). A record the parser cannot decode is yielded as
/// `Err`, never skipped: wrap the vendor error with
/// [`Error::vendor`](crate::Error::vendor) (or a `From` impl in the vendor
/// crate). One error type for every source keeps `dyn SpectrumSource`
/// usable across vendors and lets adapters such as
/// [`Centroided`](crate::Centroided) pass errors through unchanged.
/// Consumers decide what an error means: the mzML writer stops at the first
/// one unless the caller opts into
/// [`RecordErrorPolicy::Skip`](crate::RecordErrorPolicy::Skip).
///
/// # Example
///
/// ```
/// use openmassspec_core::{
/// cv, CvTerm, Error, RunMetadata, SpectrumIter, SpectrumRecord, SpectrumSource,
/// };
///
/// /// Stand-in for a vendor crate's own error type.
/// #[derive(Debug)]
/// struct DecodeError(u32);
/// impl std::fmt::Display for DecodeError {
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
/// write!(f, "scan {} is truncated", self.0)
/// }
/// }
/// impl std::error::Error for DecodeError {}
///
/// struct MyReader {
/// scans: Vec<u32>,
/// }
///
/// impl MyReader {
/// fn decode(&self, i: usize, scan: u32) -> Result<SpectrumRecord, DecodeError> {
/// if scan == 0 {
/// return Err(DecodeError(scan));
/// }
/// Ok(SpectrumRecord::new(i, scan, format!("scan={scan}"))
/// .with_ms_level(1)
/// .with_retention_time_sec(f64::from(scan))
/// .with_peaks(vec![100.0, 200.0], vec![1.0, 2.0]))
/// }
/// }
///
/// impl SpectrumSource for MyReader {
/// fn run_metadata(&self) -> RunMetadata {
/// RunMetadata::new(
/// "run.raw",
/// CvTerm::new("MS:1000563", "Thermo RAW format"),
/// CvTerm::new("MS:1000768", "Thermo nativeID format"),
/// CvTerm::new("MS:1000483", "Thermo Fisher Scientific instrument model"),
/// "my-reader",
/// "1.0.0",
/// )
/// }
///
/// fn iter_spectra<'a>(&'a mut self) -> SpectrumIter<'a> {
/// let scans = self.scans.clone();
/// Box::new(
/// scans
/// .into_iter()
/// .enumerate()
/// .map(move |(i, scan)| self.decode(i, scan).map_err(Error::vendor)),
/// )
/// }
/// }
///
/// let mut reader = MyReader { scans: vec![1, 0] };
/// let results: Vec<_> = reader.iter_spectra().collect();
/// assert!(results[0].is_ok());
/// assert!(results[1].is_err());
/// ```