Skip to main content

mzdata_meta/
traits.rs

1use std::{
2    collections::HashMap,
3    ops::{Deref, DerefMut},
4};
5
6
7use super::{
8    DataProcessing, FileDescription, InstrumentConfiguration, MassSpectrometryRun, Sample, Software, ScanSettings
9};
10
11/// Mass spectrometry data files have several facets of descriptive metadata
12pub trait MSDataFileMetadata {
13    /// The series of [`DataProcessing`] workflows applied to spectra in
14    /// this data file.
15    fn data_processings(&self) -> &Vec<DataProcessing>;
16    /// A mapping over different [`InstrumentConfiguration`] modes that spectra
17    /// were acquired under.
18    fn instrument_configurations(&self) -> &HashMap<u32, InstrumentConfiguration>;
19    /// A description of the contents and the sources for this mass spectrometry
20    /// data file.
21    fn file_description(&self) -> &FileDescription;
22    /// The series of [`Software`] applied to the data file to apply different
23    /// [`DataProcessing`] methods.
24    fn softwares(&self) -> &Vec<Software>;
25
26    /// A list of sample descriptions that were measured in this data file if
27    /// available.
28    fn samples(&self) -> &Vec<Sample>;
29
30    /// A list of scan settings in [`ScanSettings`] pre-configured by the instrument
31    ///  operator prior to the start of data acquisition.
32    ///
33    /// This information is relatively unstructured and unless manually specified will be absent
34    /// from most formats.
35    fn scan_settings(&self) -> Option<&Vec<ScanSettings>> {
36        None
37    }
38
39    /// Mutably access the [`DataProcessing`] list for this data file
40    fn data_processings_mut(&mut self) -> &mut Vec<DataProcessing>;
41    /// Mutably access the [`InstrumentConfiguration`] mapping for this data file
42    fn instrument_configurations_mut(&mut self) -> &mut HashMap<u32, InstrumentConfiguration>;
43    /// Mutably access the [`FileDescription`] description of the contents and the
44    /// sources for this mass spectrometry data file.
45    fn file_description_mut(&mut self) -> &mut FileDescription;
46    /// Mutably access the list of [`Software`] of this data file.
47    fn softwares_mut(&mut self) -> &mut Vec<Software>;
48    fn samples_mut(&mut self) -> &mut Vec<Sample>;
49
50    /// Mutably access the list of [`ScanSettings`] for this dataset. Most formats do not
51    /// possess a related concept and will not carry one.
52    fn scan_settings_mut(&mut self) -> Option<&mut Vec<ScanSettings>> {
53        None
54    }
55
56    /// Copy the metadata from another [`MSDataFileMetadata`] implementation into
57    /// this one.
58    fn copy_metadata_from(&mut self, source: &impl MSDataFileMetadata)
59    where
60        Self: Sized,
61    {
62        *self.data_processings_mut() = source.data_processings().clone();
63        *self.instrument_configurations_mut() = source.instrument_configurations().clone();
64        *self.file_description_mut() = source.file_description().clone();
65        *self.softwares_mut() = source.softwares().clone();
66        *self.samples_mut() = source.samples().clone();
67        self.set_spectrum_count_hint(source.spectrum_count_hint());
68
69        if let Some(settings) = source.scan_settings() {
70            match self.scan_settings_mut() {
71                Some(mine) => *mine = settings.clone(),
72                None => {
73                    log::debug!("Cannot store scan settings on this type of data file");
74                }
75            }
76        }
77
78        match source.run_description() {
79            Some(run) => {
80                let desc = self.run_description_mut();
81                if let Some(r) = desc {
82                    *r = run.clone();
83                };
84            }
85            None => {
86                let mut desc = self.run_description_mut();
87                desc.take();
88            }
89        }
90    }
91
92    /// A hint about how many spectra are in this data file
93    fn spectrum_count_hint(&self) -> Option<u64> {
94        None
95    }
96
97    fn set_spectrum_count_hint(&mut self, _value: Option<u64>) {}
98
99    /// Access the [`MassSpectrometryRun`] metadata record if it is available
100    fn run_description(&self) -> Option<&MassSpectrometryRun> {
101        None
102    }
103
104    /// Mutably access the [`MassSpectrometryRun`] metadata record if it is available
105    fn run_description_mut(&mut self) -> Option<&mut MassSpectrometryRun> {
106        None
107    }
108
109    /// Get the name of the primary source file, if available
110    fn source_file_name(&self) -> Option<&str> {
111        self.file_description()
112            .source_files
113            .first()
114            .map(|s| s.name.as_str())
115    }
116}
117
118/// A helper data structure for implementing [`MSDataFileMetadata`] in a single common
119/// implementation.
120#[derive(Debug, Default, Clone)]
121pub struct FileMetadataConfig {
122    /// The description of the file's contents and the previous data files that were
123    /// consumed to produce it.
124    pub(crate) file_description: FileDescription,
125
126    /// A mapping of different instrument configurations (source, analyzer, detector) components
127    /// by ID string.
128    pub(crate) instrument_configurations: HashMap<u32, InstrumentConfiguration>,
129
130    /// The different software components that were involved in the processing and creation of this
131    /// file.
132    pub(crate) softwares: Vec<Software>,
133
134    pub(crate) samples: Vec<Sample>,
135
136    pub(crate) scan_settings: Vec<ScanSettings>,
137
138    /// The data processing and signal transformation operations performed on the raw data in previous
139    /// source files to produce this file's contents.
140    pub(crate) data_processings: Vec<DataProcessing>,
141
142    // MS run attributes
143    pub(crate) run: MassSpectrometryRun,
144    pub(crate) num_spectra: Option<u64>,
145}
146
147impl FileMetadataConfig {
148    #[allow(clippy::too_many_arguments)]
149    pub fn new(
150        file_description: FileDescription,
151        instrument_configurations: HashMap<u32, InstrumentConfiguration>,
152        softwares: Vec<Software>,
153        samples: Vec<Sample>,
154        scan_settings: Vec<ScanSettings>,
155        data_processings: Vec<DataProcessing>,
156        run: MassSpectrometryRun,
157        num_spectra: Option<u64>,
158    ) -> Self {
159        Self {
160            file_description,
161            instrument_configurations,
162            softwares,
163            samples,
164            scan_settings,
165            data_processings,
166            run,
167            num_spectra,
168        }
169    }
170}
171
172impl<T> From<&T> for FileMetadataConfig
173where
174    T: MSDataFileMetadata,
175{
176    fn from(value: &T) -> Self {
177        let mut this = Self::default();
178        this.copy_metadata_from(value);
179        this
180    }
181}
182
183impl MSDataFileMetadata for FileMetadataConfig {
184    crate::impl_metadata_trait!();
185
186    fn scan_settings(&self) -> Option<&Vec<ScanSettings>> {
187        Some(&self.scan_settings)
188    }
189
190    fn scan_settings_mut(&mut self) -> Option<&mut Vec<ScanSettings>> {
191        Some(&mut self.scan_settings)
192    }
193
194    fn run_description(&self) -> Option<&MassSpectrometryRun> {
195        Some(&self.run)
196    }
197
198    fn run_description_mut(&mut self) -> Option<&mut MassSpectrometryRun> {
199        Some(&mut self.run)
200    }
201
202    fn set_spectrum_count_hint(&mut self, _value: Option<u64>) {
203        self.num_spectra = _value
204    }
205
206    fn spectrum_count_hint(&self) -> Option<u64> {
207        self.num_spectra
208    }
209}
210
211#[macro_export]
212/// Assumes a field for the non-`Option` facets of the [`MSDataFileMetadata`]
213/// implementation are present. Passing an extra level `extended` token implements
214/// the optional methods.
215macro_rules! impl_metadata_trait {
216    (extended) => {
217        $crate::impl_metadata_trait();
218
219        fn spectrum_count_hint(&self) -> Option<u64> {
220            self.num_spectra
221        }
222
223        fn run_description(&self) -> Option<&$crate::MassSpectrometryRun> {
224            Some(&self.run)
225        }
226
227        fn run_description_mut(&mut self) -> Option<&mut $crate::MassSpectrometryRun> {
228            Some(&mut self.run)
229        }
230    };
231    () => {
232        fn data_processings(&self) -> &Vec<$crate::DataProcessing> {
233            &self.data_processings
234        }
235
236        fn instrument_configurations(
237            &self,
238        ) -> &std::collections::HashMap<u32, $crate::InstrumentConfiguration> {
239            &self.instrument_configurations
240        }
241        fn file_description(&self) -> &$crate::FileDescription {
242            &self.file_description
243        }
244        fn softwares(&self) -> &Vec<$crate::Software> {
245            &self.softwares
246        }
247
248        fn data_processings_mut(&mut self) -> &mut Vec<$crate::DataProcessing> {
249            &mut self.data_processings
250        }
251
252        fn instrument_configurations_mut(
253            &mut self,
254        ) -> &mut std::collections::HashMap<u32, $crate::InstrumentConfiguration> {
255            &mut self.instrument_configurations
256        }
257
258        fn file_description_mut(&mut self) -> &mut $crate::FileDescription {
259            &mut self.file_description
260        }
261
262        fn softwares_mut(&mut self) -> &mut Vec<$crate::Software> {
263            &mut self.softwares
264        }
265
266        fn samples(&self) -> &Vec<$crate::Sample> {
267            &self.samples
268        }
269
270        fn samples_mut(&mut self) -> &mut Vec<$crate::Sample> {
271            &mut self.samples
272        }
273    };
274}
275
276#[macro_export]
277/// Delegates the implementation of [`MSDataFileMetadata`] to a member. Passing an extra
278/// level `extended` token implements the optional methods.
279macro_rules! delegate_impl_metadata_trait {
280
281    (expr, $self:ident => $impl:tt, &mut => $mut_impl:tt) => {
282
283        fn data_processings(&self) -> &Vec<$crate::DataProcessing> {
284            let $self = self;
285            let step = $impl;
286            step.data_processings()
287        }
288
289        fn instrument_configurations(&self) -> &std::collections::HashMap<u32, $crate::InstrumentConfiguration> {
290            let $self = self;
291            let step = $impl;
292            step.instrument_configurations()
293        }
294
295        fn file_description(&self) -> &$crate::FileDescription {
296            let $self = self;
297            let step = $impl;
298            step.file_description()
299        }
300
301        fn softwares(&self) -> &Vec<$crate::Software> {
302            let $self = self;
303            let step = $impl;
304            step.softwares()
305        }
306
307        fn samples(&self) -> &Vec<$crate::Sample> {
308            let $self = self;
309            let step = $impl;
310            step.samples()
311        }
312
313        fn data_processings_mut(&mut self) -> &mut Vec<$crate::DataProcessing> {
314            let $self = self;
315            let step = $mut_impl;
316            step.data_processings_mut()
317        }
318
319        fn instrument_configurations_mut(&mut self) -> &mut std::collections::HashMap<u32, $crate::InstrumentConfiguration> {
320            let $self = self;
321            let step = $mut_impl;
322            step.instrument_configurations_mut()
323        }
324
325        fn file_description_mut(&mut self) -> &mut $crate::FileDescription {
326            let $self = self;
327            let step = $mut_impl;
328            step.file_description_mut()
329        }
330
331        fn softwares_mut(&mut self) -> &mut Vec<$crate::Software> {
332            let $self = self;
333            let step = $mut_impl;
334            step.softwares_mut()
335        }
336
337        fn samples_mut(&mut self) -> &mut Vec<$crate::Sample> {
338            let $self = self;
339            let step = $mut_impl;
340            step.samples_mut()
341        }
342
343        fn spectrum_count_hint(&self) -> Option<u64> {
344            let $self = self;
345            let step = $impl;
346            step.spectrum_count_hint()
347        }
348
349        fn run_description(&self) -> Option<&$crate::MassSpectrometryRun> {
350            let $self = self;
351            let step = $impl;
352            step.run_description()
353        }
354
355        fn run_description_mut(&mut self) -> Option<&mut $crate::MassSpectrometryRun> {
356            let $self = self;
357            let step = $mut_impl;
358            step.run_description_mut()
359        }
360
361        fn source_file_name(&self) -> Option<&str> {
362            let $self = self;
363            let step = $impl;
364            step.source_file_name()
365        }
366
367        fn scan_settings(&self) -> Option<&Vec<$crate::ScanSettings>> {
368            let $self = self;
369            let step = $impl;
370            step.scan_settings()
371        }
372
373        fn scan_settings_mut(&mut self) -> Option<&mut Vec<$crate::ScanSettings>> {
374            let $self = self;
375            let step = $mut_impl;
376            step.scan_settings_mut()
377        }
378    };
379    ($src:tt, extended) => {
380        $crate::delegate_impl_metadata_trait($src);
381    };
382    ($src:tt) => {
383        $crate::delegate_impl_metadata_trait!(expr, this => { &this.$src }, &mut => { &mut this.$src });
384    };
385}
386
387impl<T: MSDataFileMetadata> MSDataFileMetadata for Box<T> {
388    delegate_impl_metadata_trait!(expr, x => { x.deref() }, &mut => { x.deref_mut() });
389}
390
391/// [`MSDataFileMetadata`] may be accessed on [`std::sync::Arc`] wrapping types that implement the trait,
392/// but attempting to use the mutable methods will panic if the underlying instance is already shared.
393impl<T: MSDataFileMetadata> MSDataFileMetadata for std::sync::Arc<T> {
394    delegate_impl_metadata_trait!(expr, x => { x.deref() }, &mut => { std::sync::Arc::get_mut(x).unwrap_or_else(|| panic!("Attempting to modify `MSDataFileMetadata` via a shared reference")) });
395}