use std::{
io::{BufRead, Write},
sync::Arc,
};
use quick_xml::events::{BytesStart, Event};
use crate::{
mzml::{
attributes::{AttributeValue, RUN_ATTRIBUTES},
cvparam::{CVParam, HasCVParams, HasParamGroupRefs, UserParam},
filedescription::SourceFileRef,
instrument::InstrumentConfigurationRef,
referenceableparamgroup::ReferenceableParamGroupRef,
sample::SampleRef,
},
ChromatogramList, FatalParseError, ParseError, Spectrum, SpectrumList, Tag,
};
use super::{writer::Writer, MzMLReader, MzMLTag};
pub struct Run {
param_group_refs: Vec<ReferenceableParamGroupRef>,
cv_params: Vec<CVParam>,
user_params: Vec<UserParam>,
default_instrument_configuration_ref: InstrumentConfigurationRef,
id: Arc<str>,
default_source_file_ref: Option<SourceFileRef>,
sample_ref: Option<SampleRef>,
start_time_stamp: Option<String>,
pub(crate) spectrum_list: Option<SpectrumList>,
pub(crate) chromatogram_list: Option<ChromatogramList>,
}
impl Clone for Run {
fn clone(&self) -> Self {
Self {
param_group_refs: self.param_group_refs.clone(),
cv_params: self.cv_params.clone(),
user_params: self.user_params.clone(),
default_instrument_configuration_ref: self.default_instrument_configuration_ref.clone(),
id: self.id.clone(),
default_source_file_ref: self.default_source_file_ref.clone(),
sample_ref: self.sample_ref.clone(),
start_time_stamp: self.start_time_stamp.clone(),
spectrum_list: self.spectrum_list.clone(),
chromatogram_list: self.chromatogram_list.clone(),
}
}
}
impl Run {
pub fn new(id: &str, default_instrument_configuration_ref: InstrumentConfigurationRef) -> Self {
Run {
param_group_refs: Vec::new(),
cv_params: Vec::new(),
user_params: Vec::new(),
id: id.into(),
default_instrument_configuration_ref,
default_source_file_ref: None,
sample_ref: None,
start_time_stamp: None,
spectrum_list: None,
chromatogram_list: None,
}
}
pub fn set_default_source_file_ref(&mut self, source_file_ref: SourceFileRef) {
self.default_source_file_ref = Some(source_file_ref);
}
pub fn set_sample_ref(&mut self, sample_ref: SampleRef) {
self.sample_ref = Some(sample_ref);
}
pub fn set_start_time_stamp(&mut self, start_time_stamp: &str) {
self.start_time_stamp = Some(start_time_stamp.into());
}
pub fn spectrum(&self, index: usize) -> Option<&Arc<Spectrum>> {
self.spectrum_list.as_ref().unwrap().spectrum(index)
}
pub fn num_spectra(&self) -> usize {
self.spectrum_list.as_ref().unwrap().len()
}
pub fn set_spectrum_list(&mut self, spectrum_list: SpectrumList) {
self.spectrum_list = Some(spectrum_list)
}
pub fn spectrum_list(&self) -> Option<&SpectrumList> {
self.spectrum_list.as_ref()
}
pub fn spectrum_list_mut(&mut self) -> Option<&mut SpectrumList> {
self.spectrum_list.as_mut()
}
pub fn chromatogram_list(&self) -> Option<&ChromatogramList> {
self.chromatogram_list.as_ref()
}
}
impl MzMLTag for Run {
fn parse_start_tag<B: BufRead>(
parser: &mut MzMLReader<B>,
start_event: &BytesStart,
) -> Result<Option<Self>, FatalParseError>
where
Self: std::marker::Sized,
{
if start_event.name().as_ref() != b"run" {
return Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing Run",
start_event,
)));
}
let attributes = parser.process_attributes(Tag::Run, &RUN_ATTRIBUTES, start_event)?;
let id = match attributes.get("id") {
Some(AttributeValue::String(id)) => parser.parse_string(Tag::Run, id).unwrap(),
_ => "",
};
let default_instrument_configuration_ref =
match attributes.get("defaultInstrumentConfigurationRef") {
Some(AttributeValue::String(instrument_ref_id)) => {
let instrument_ref_id = parser
.parse_string(Tag::Run, instrument_ref_id)
.unwrap_or("");
let configuration_ref =
parser.instrument_configuration_ref(instrument_ref_id.as_bytes());
configuration_ref.unwrap_or_else(|| {
InstrumentConfigurationRef::Id(instrument_ref_id.to_string())
})
}
_ => InstrumentConfigurationRef::Id("".to_string()),
};
let mut run = Run::new(id, default_instrument_configuration_ref);
if let Some(attribute) = attributes.get("defaultSourceFileRef") {
let source_file_ref = match attribute {
AttributeValue::String(source_file_ref_id) => {
let source_file_ref_id = parser
.parse_string(Tag::Run, source_file_ref_id)
.unwrap_or("");
let source_file_ref = parser.source_file_ref(source_file_ref_id.as_bytes());
source_file_ref
.unwrap_or_else(|| SourceFileRef::Id(source_file_ref_id.to_string()))
}
_ => SourceFileRef::Id("".to_string()),
};
run.set_default_source_file_ref(source_file_ref);
}
if let Some(AttributeValue::String(value)) = attributes.get("startTimeStamp") {
run.start_time_stamp = parser
.parse_string(Tag::UserParam, value)
.map(|value| value.to_string());
}
parser.breadcrumbs.push_back((Tag::Run, None)); Ok(Some(run))
}
fn parse_xml<B: BufRead>(
&mut self,
parser: &mut MzMLReader<B>,
buffer: &mut Vec<u8>,
) -> Result<(), FatalParseError> {
loop {
buffer.clear();
let next_event = parser.next(buffer)?;
match next_event {
Event::Start(start_event) | Event::Empty(start_event) => {
match start_event.name().as_ref() {
b"cvParam" => {
if let Some(cv_param) = CVParam::parse_start_tag(parser, &start_event)?
{
self.cv_params.push(cv_param);
}
}
b"referenceableParamGroupRef" => {
let param_group_ref =
ReferenceableParamGroupRef::parse_start_tag(parser, &start_event)?;
self.param_group_refs.push(param_group_ref);
}
b"userParam" => {
if let Some(user_param) =
UserParam::parse_start_tag(parser, &start_event)?
{
self.user_params.push(user_param);
}
}
b"spectrumList" => {
if let Some(mut spectrum_list) =
SpectrumList::parse_start_tag(parser, &start_event)?
{
spectrum_list.parse_xml(parser, buffer)?;
self.spectrum_list = Some(spectrum_list);
}
}
b"chromatogramList" => {
if let Some(mut chromatogram_list) =
ChromatogramList::parse_start_tag(parser, &start_event)?
{
chromatogram_list.parse_xml(parser, buffer)?;
self.chromatogram_list = Some(chromatogram_list);
}
}
_ => parser.errors.push_back(ParseError::UnexpectedTag(format!(
"{:?} unexpected when processing {:?}",
std::str::from_utf8(start_event.name().as_ref()),
Self::tag()
))),
}
}
Event::End(end_event) => {
if let b"run" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag("run".to_string()));
}
_ => {}
}
}
Ok(())
}
fn tag() -> crate::Tag {
Tag::Run
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
let mut elem = BytesStart::new("run");
elem.push_attribute(("id", self.id.as_ref()));
match self.default_instrument_configuration_ref {
InstrumentConfigurationRef::Id(ref id) => {
elem.push_attribute(("defaultInstrumentConfigurationRef", id.as_str()))
}
InstrumentConfigurationRef::Ref(ref instrument_configuration) => elem.push_attribute((
"defaultInstrumentConfigurationRef",
instrument_configuration.id(),
)),
}
if let Some(ref default_source_file_ref) = self.default_source_file_ref {
match default_source_file_ref {
SourceFileRef::Id(ref id) => {
elem.push_attribute(("defaultSourceFileRef", id.as_str()))
}
SourceFileRef::Ref(ref source_file) => {
elem.push_attribute(("defaultSourceFileRef", source_file.id()))
}
}
}
if let Some(ref sample_ref) = self.sample_ref {
match sample_ref {
SampleRef::Id(ref id) => elem.push_attribute(("sampleRef", id.as_str())),
SampleRef::Ref(ref sample) => elem.push_attribute(("sampleRef", sample.id())),
}
}
if let Some(ref start_time_stamp) = self.start_time_stamp {
elem.push_attribute(("startTimeStamp", start_time_stamp.as_str()));
}
writer.write_event(Event::Start(elem))?;
if let Some(ref spectrum_list) = self.spectrum_list {
spectrum_list.write_xml(writer)?;
}
writer.end_tag("run")
}
}
impl HasCVParams for Run {
fn add_cv_param(&mut self, param: CVParam) {
self.cv_params.push(param);
}
fn cv_params(&self) -> &Vec<CVParam> {
&self.cv_params
}
fn cv_params_mut(&mut self) -> &mut Vec<CVParam> {
self.cv_params.as_mut()
}
fn add_user_param(&mut self, param: UserParam) {
self.user_params.push(param);
}
fn user_params(&self) -> &Vec<UserParam> {
&self.user_params
}
}
impl HasParamGroupRefs for Run {
fn add_param_group_ref(&mut self, param_group_ref: ReferenceableParamGroupRef) {
self.param_group_refs.push(param_group_ref);
}
fn param_group_refs(&self) -> &Vec<ReferenceableParamGroupRef> {
&self.param_group_refs
}
}