Skip to content

Commit

Permalink
fix: fixed crash while mgf parsing (#12)
Browse files Browse the repository at this point in the history
* Fixed rare crash while mgf parsing
* Fixed silent error that returns an unfinished spectrum
* Handle charge when the sign is at the tail
  • Loading branch information
douweschulte authored Oct 16, 2024
1 parent 2ef3bb5 commit 93c19a9
Showing 1 changed file with 51 additions and 47 deletions.
98 changes: 51 additions & 47 deletions src/io/mgf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use std::convert::TryInto;
use std::fs;
use std::io::{self, prelude::*, BufWriter, SeekFrom};
use std::marker::PhantomData;
use std::mem;
use std::str;

use log::warn;
Expand Down Expand Up @@ -300,6 +299,7 @@ impl<R: io::Read, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> MGFReade
true
} else if line.contains('=') {
let (key, value) = line.split_once('=').unwrap();
let value = value.trim();
builder.empty_metadata = false;
match key {
"TITLE" => builder.description.id = value.to_string(),
Expand Down Expand Up @@ -363,26 +363,44 @@ impl<R: io::Read, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> MGFReade
..Default::default()
});
}
"CHARGE" => match value.parse() {
Ok(z) => {
if let Some(ion) = builder
.description
.precursor
.get_or_insert_with(|| Precursor::default())
.iter_mut()
.last()
{
ion.charge = Some(z);
}
}
Err(e) => {
"CHARGE" => {
let (sign, value, tail_sign) = if let Some(stripped) = value.strip_suffix('+') {
(1, stripped, true)
} else if let Some(stripped) = value.strip_suffix('-') {
(-1, stripped, true)
} else {
(1, value, false)
};

if tail_sign && (value.starts_with('-') || value.starts_with('+')) {
self.state = MGFParserState::Error;
self.error = Some(MGFError::MalformedHeaderLine(format!(
"Could not parse CHARGE header {value} : {e}"
"Could not parse CHARGE header {value}"
)));
return false;
}
},

match value.parse::<i32>() {
Ok(z) => {
if let Some(ion) = builder
.description
.precursor
.get_or_insert_with(Precursor::default)
.iter_mut()
.last()
{
ion.charge = Some(sign * z);
}
}
Err(e) => {
self.state = MGFParserState::Error;
self.error = Some(MGFError::MalformedHeaderLine(format!(
"Could not parse CHARGE header {value} : {e}"
)));
return false;
}
}
}
&_ => {
builder
.description
Expand Down Expand Up @@ -446,16 +464,11 @@ impl<R: io::Read, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> MGFReade
/// Read the next spectrum from the file, if there is one.
pub fn read_next(&mut self) -> Option<MultiLayerSpectrum<C, D>> {
let mut builder = SpectrumBuilder::<C, D>::default();
match self._parse_into(&mut builder) {
Ok((_, started_spectrum)) => {
if started_spectrum && !builder.is_empty() {
Some(builder.into())
} else {
None
}
}
Err(_err) => None,
}
self._parse_into(&mut builder)
.ok()
.and_then(|(_, started_spectrum)| {
(started_spectrum && !builder.is_empty()).then(|| builder.into())
})
}

/// Read the next spectrum's contents directly into the passed [`SpectrumBuilder`].
Expand All @@ -471,12 +484,7 @@ impl<R: io::Read, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> MGFReade
while work {
buffer.clear();
let b = match self.read_line(&mut buffer) {
Ok(b) => {
if b == 0 {
work = false;
}
b
}
Ok(b) => b,
Err(err) => {
self.state = MGFParserState::Error;
return Err(MGFError::IOError(err));
Expand All @@ -491,10 +499,9 @@ impl<R: io::Read, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> MGFReade
}

let line = buffer.trim();
let n = line.len();

// Skip empty lines
if n == 0 {
if line.is_empty() {
continue;
}

Expand All @@ -508,12 +515,13 @@ impl<R: io::Read, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting> MGFReade
MGFParserState::Between => self.handle_between(line),
MGFParserState::Done => false,
MGFParserState::Error => {
let mut err = None;
mem::swap(&mut self.error, &mut err);
self.error = None;
return Err(err.unwrap());
return Err(self.error.take().unwrap_or(MGFError::NoError));
}
};

if self.state == MGFParserState::Error {
return Err(self.error.take().unwrap_or(MGFError::NoError));
}
}
Ok((offset, had_begin_ions))
}
Expand Down Expand Up @@ -672,8 +680,7 @@ impl<R: SeekRead, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting>

/// Retrieve a spectrum by it's integer index
fn get_spectrum_by_index(&mut self, index: usize) -> Option<MultiLayerSpectrum<C, D>> {
let (_id, offset) = self.index.get_index(index)?;
let byte_offset = offset;
let (_id, byte_offset) = self.index.get_index(index)?;
let start = self
.handle
.stream_position()
Expand All @@ -682,13 +689,10 @@ impl<R: SeekRead, C: CentroidPeakAdapting, D: DeconvolutedPeakAdapting>
let result = self.read_next();
self.seek(SeekFrom::Start(start))
.expect("Failed to restore offset");
match result {
Some(mut scan) => {
scan.description.index = index;
Some(scan)
}
None => None,
}
result.map(|mut scan| {
scan.description.index = index;
scan
})
}

/// Return the data stream to the beginning
Expand Down

0 comments on commit 93c19a9

Please sign in to comment.