1use std::fs::File;
12use std::io::BufWriter;
13
14use openmassspec_core::{
15 write_indexed_mzml, write_mzml, Activation, Analyzer, ChromatogramRecord, CvTerm,
16 MobilityArrayKind, MsPower, Polarity, PrecursorInfo, RunMetadata, ScanMode, SpectrumRecord,
17 SpectrumSource,
18};
19
20struct SampleSource {
21 meta: RunMetadata,
22 spectra: Vec<SpectrumRecord>,
23 chroms: Vec<ChromatogramRecord>,
24 cursor: usize,
25}
26
27impl SampleSource {
28 fn new() -> Self {
29 let meta = RunMetadata {
30 source_file_name: "sample.raw".into(),
31 source_file_format: CvTerm::new("MS:1000563", "Thermo RAW format"),
32 native_id_format: CvTerm::new("MS:1000768", "Thermo nativeID format"),
33 instrument: CvTerm::new("MS:1001911", "Q Exactive"),
34 instrument_serial_number: Some("SN-EXAMPLE-001".into()),
35 software_name: "openmassspec-core-sample".into(),
36 software_version: env!("CARGO_PKG_VERSION").into(),
37 start_timestamp: Some("2026-06-01T14:30:00Z".into()),
40 mobility_array_kind: Some(MobilityArrayKind::InverseReducedVsPerCm2),
41 analyzers: vec![Analyzer::FTMS, Analyzer::TOFMS],
46 };
47 let ms1 = SpectrumRecord {
48 index: 0,
49 scan_number: 1,
50 native_id: "controllerType=0 controllerNumber=1 scan=1".into(),
51 ms_level: MsPower::Ms1.ms_level(),
52 polarity: Some(Polarity::Positive),
53 scan_mode: Some(ScanMode::Centroid),
54 analyzer: Some(Analyzer::FTMS),
55 filter: Some("FTMS + p ESI Full ms".into()),
56 retention_time_sec: 0.123 * 60.0,
57 total_ion_current: None,
58 base_peak_mz: None,
59 base_peak_intensity: None,
60 low_mz: None,
61 high_mz: None,
62 ion_injection_time_ms: Some(20.0),
63 inv_mobility: None,
64 faims_cv: None,
65 precursor: None,
66 mz: vec![100.0, 200.0, 300.0],
67 intensity: vec![1.0, 5.0, 2.0],
68 inv_mobility_per_peak: None,
69 };
70 let ms2 = SpectrumRecord {
71 index: 1,
72 scan_number: 2,
73 native_id: "controllerType=0 controllerNumber=1 scan=2".into(),
74 ms_level: MsPower::Ms2.ms_level(),
75 polarity: Some(Polarity::Positive),
76 scan_mode: Some(ScanMode::Centroid),
77 analyzer: Some(Analyzer::FTMS),
78 filter: Some("FTMS + p ESI d Full ms2 200.00@hcd28.00".into()),
79 retention_time_sec: 0.5 * 60.0,
80 total_ion_current: Some(123.45),
81 base_peak_mz: Some(150.5),
82 base_peak_intensity: Some(99.0),
83 low_mz: Some(100.0),
84 high_mz: Some(180.0),
85 ion_injection_time_ms: Some(50.0),
86 inv_mobility: None,
87 faims_cv: Some(-45.0),
88 precursor: Some(PrecursorInfo {
89 target_mz: Some(200.0),
90 selected_mz: Some(200.001),
91 isolation_width: Some(2.0),
92 charge: Some(2),
93 intensity: None,
94 collision_energy: Some(28.0),
95 ce_is_nce: true,
96 precursor_native_id: Some("controllerType=0 controllerNumber=1 scan=1".into()),
97 activation: Some(Activation::CID),
98 analyzer: Some(Analyzer::FTMS),
99 ccs: Some(153.4),
102 }),
103 mz: vec![150.5, 160.0],
104 intensity: vec![99.0, 50.0],
105 inv_mobility_per_peak: None,
106 };
107 let ms1_mobility = SpectrumRecord {
112 index: 2,
113 scan_number: 3,
114 native_id: "frame=1 scan=0".into(),
115 ms_level: MsPower::Ms1.ms_level(),
116 polarity: Some(Polarity::Positive),
117 scan_mode: Some(ScanMode::Centroid),
118 analyzer: Some(Analyzer::TOFMS),
119 filter: None,
120 retention_time_sec: 0.75 * 60.0,
121 total_ion_current: None,
122 base_peak_mz: None,
123 base_peak_intensity: None,
124 low_mz: None,
125 high_mz: None,
126 ion_injection_time_ms: None,
127 inv_mobility: Some(0.95),
128 faims_cv: None,
129 precursor: None,
130 mz: vec![120.0, 240.0, 360.0],
131 intensity: vec![3.0, 7.0, 4.0],
132 inv_mobility_per_peak: Some(vec![0.92, 0.95, 0.98]),
133 };
134 let tic = ChromatogramRecord {
138 index: 0,
139 id: "TIC".into(),
140 chromatogram_type: Some(CvTerm::new("MS:1000235", "total ion current chromatogram")),
141 precursor_mz: None,
142 product_mz: None,
143 time_sec: vec![0.0, 30.0, 45.0],
144 intensity: vec![120.0, 340.0, 210.0],
145 };
146 let srm = ChromatogramRecord {
147 index: 1,
148 id: "SRM SIC Q1=524.3 Q3=136.1".into(),
149 chromatogram_type: Some(CvTerm::new(
150 "MS:1001473",
151 "selected reaction monitoring chromatogram",
152 )),
153 precursor_mz: Some(524.3),
154 product_mz: Some(136.1),
155 time_sec: vec![0.0, 30.0],
156 intensity: vec![50.0, 80.0],
157 };
158 Self {
159 meta,
160 spectra: vec![ms1, ms2, ms1_mobility],
161 chroms: vec![tic, srm],
162 cursor: 0,
163 }
164 }
165}
166
167impl SpectrumSource for SampleSource {
168 fn run_metadata(&self) -> RunMetadata {
169 self.meta.clone()
170 }
171
172 fn iter_spectra<'a>(&'a mut self) -> Box<dyn Iterator<Item = SpectrumRecord> + 'a> {
173 self.cursor = 0;
174 Box::new(std::iter::from_fn(move || {
175 let rec = self.spectra.get(self.cursor).cloned();
176 if rec.is_some() {
177 self.cursor += 1;
178 }
179 rec
180 }))
181 }
182
183 fn iter_chromatograms<'a>(&'a mut self) -> Box<dyn Iterator<Item = ChromatogramRecord> + 'a> {
184 Box::new(self.chroms.clone().into_iter())
185 }
186
187 fn spectrum_count_hint(&self) -> Option<usize> {
188 Some(self.spectra.len())
189 }
190}
191
192fn main() -> std::io::Result<()> {
193 let mut args = std::env::args().skip(1);
194 let plain_path = args.next().unwrap_or_else(|| "sample_plain.mzML".into());
195 let indexed_path = args.next().unwrap_or_else(|| "sample_indexed.mzML".into());
196
197 let mut src = SampleSource::new();
198 let mut plain = BufWriter::new(File::create(&plain_path)?);
199 write_mzml(&mut src, &mut plain)
200 .map_err(|e| std::io::Error::other(format!("write_mzml: {e}")))?;
201
202 let mut indexed = BufWriter::new(File::create(&indexed_path)?);
203 write_indexed_mzml(&mut src, &mut indexed)
204 .map_err(|e| std::io::Error::other(format!("write_indexed_mzml: {e}")))?;
205
206 eprintln!("wrote {plain_path} and {indexed_path}");
207 Ok(())
208}