1pub mod mzml;
2pub mod prefetch;
3pub mod tims_calibration;
4
5#[cfg(feature = "tdf")]
6pub mod bruker;
7
8#[cfg(feature = "thermo")]
9pub mod thermo;
10
11use std::path::Path;
12
13use crate::config::FileConfig;
14use crate::error::KothError;
15use crate::models::Spectrum;
16
17pub type SpectrumStream = Box<dyn Iterator<Item = Result<Spectrum, KothError>> + Send>;
20
21pub fn stream_spectra(path: &Path, file: &FileConfig) -> Result<SpectrumStream, KothError> {
24 match detect_format(path) {
25 InputFormat::Mzml | InputFormat::MzmlGz => mzml::stream_mzml_results(path),
26 #[cfg(feature = "tdf")]
27 InputFormat::BrukerD => Ok(Box::new(bruker::stream_bruker(path, file))),
28 #[cfg(feature = "thermo")]
29 InputFormat::ThermoRaw => thermo::stream_thermo(path),
30 _ => read_spectra(path, file)
33 .map(|spectra| Box::new(spectra.into_iter().map(Ok)) as SpectrumStream),
34 }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum InputFormat {
45 Mzml,
47 MzmlGz,
49 BrukerD,
51 ThermoRaw,
53 Unsupported,
55}
56
57pub fn detect_format(path: &Path) -> InputFormat {
71 let name = path
72 .file_name()
73 .and_then(|n| n.to_str())
74 .unwrap_or("")
75 .to_lowercase();
76
77 if name.ends_with(".mzml.gz") {
78 InputFormat::MzmlGz
79 } else if name.ends_with(".mzml") {
80 InputFormat::Mzml
81 } else if name.ends_with(".d") && path.is_dir() {
82 InputFormat::BrukerD
83 } else if name.ends_with(".raw") {
84 InputFormat::ThermoRaw
85 } else {
86 InputFormat::Unsupported
87 }
88}
89
90pub fn read_spectra(path: &Path, file: &FileConfig) -> Result<Vec<Spectrum>, KothError> {
97 match detect_format(path) {
98 InputFormat::Mzml | InputFormat::MzmlGz => mzml::read_mzml(path),
99 InputFormat::BrukerD => read_bruker_inner(path, file),
100 InputFormat::ThermoRaw => read_thermo_inner(path),
101 InputFormat::Unsupported => Err(KothError::UnsupportedFormat(format!(
102 "'{}' is not a supported input format. Use .mzML, a Bruker .d directory, or a Thermo .raw file.",
103 path.display()
104 ))),
105 }
106}
107
108#[cfg(feature = "thermo")]
109fn read_thermo_inner(path: &Path) -> Result<Vec<Spectrum>, KothError> {
110 thermo::read_thermo(path)
111}
112
113#[cfg(not(feature = "thermo"))]
114fn read_thermo_inner(_path: &Path) -> Result<Vec<Spectrum>, KothError> {
115 Err(KothError::UnsupportedFormat(
116 "Thermo .raw support requires the 'thermo' feature flag. Rebuild with the default features (or --features thermo)."
117 .into(),
118 ))
119}
120
121#[cfg(feature = "tdf")]
122fn read_bruker_inner(path: &Path, file: &FileConfig) -> Result<Vec<Spectrum>, KothError> {
123 bruker::read_bruker(path, file)
124}
125
126#[cfg(not(feature = "tdf"))]
127fn read_bruker_inner(_path: &Path, _file: &FileConfig) -> Result<Vec<Spectrum>, KothError> {
128 Err(KothError::UnsupportedFormat(
129 "Bruker .d support requires the 'tdf' feature flag. Rebuild with --features tdf.".into(),
130 ))
131}