use std::{
io::{BufRead, Write},
sync::Arc,
};
use quick_xml::events::{BytesStart, Event};
use crate::{
error::Breadcrumbs,
mzml::{
attributes::AttributeValue,
cvparam::{CVParam, HasCVParams, HasParamGroupRefs, UserParam},
referenceableparamgroup::ReferenceableParamGroupRef,
software::SoftwareRef,
},
FatalParseError, ParseError, Tag,
};
use super::{
attributes::{ID_ATTRIBUTE, LIST_ATTRIBUTES, PROCESSING_METHOD_ATTRIBUTES},
writer::Writer,
MzMLReader, MzMLTag,
};
#[derive(Debug)]
pub enum DataProcessingRef {
Id(String),
Ref(Arc<DataProcessing>),
}
impl DataProcessingRef {
pub fn id(&self) -> &str {
match self {
DataProcessingRef::Id(id) => id,
DataProcessingRef::Ref(dp_ref) => dp_ref.id(),
}
}
}
impl Clone for DataProcessingRef {
fn clone(&self) -> Self {
match self {
Self::Id(arg0) => Self::Id(arg0.clone()),
Self::Ref(arg0) => Self::Ref(arg0.clone()),
}
}
}
pub struct DataProcessingList {
pub(crate) list: Vec<Arc<DataProcessing>>,
}
impl DataProcessingList {
fn new(count: usize) -> Self {
DataProcessingList {
list: Vec::with_capacity(count),
}
}
}
impl MzMLTag for DataProcessingList {
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().local_name().as_ref() != b"dataProcessingList" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing DataProcessingList",
start_event,
)))
} else {
let attributes = parser.process_attributes(
Tag::DataProcessingList,
&LIST_ATTRIBUTES,
start_event,
)?;
let count = match attributes.get("count") {
Some(&AttributeValue::Integer(count)) => count as usize,
_ => 0,
};
parser
.breadcrumbs
.push_back((Tag::DataProcessingList, None));
Ok(Some(DataProcessingList::new(count)))
}
}
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"dataProcessing" => {
if let Some(mut data_processing) =
DataProcessing::parse_start_tag(parser, &start_event)?
{
data_processing.parse_xml(parser, buffer)?;
self.list.push(Arc::new(data_processing));
}
}
_ => parser.errors.push_back(ParseError::UnexpectedTag(format!(
"{:?} unexpected when processing {:?}",
std::str::from_utf8(start_event.name().as_ref()),
Tag::DataProcessingList
))),
}
}
Event::End(end_event) => {
if let b"dataProcessingList" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag(
"dataProcessingList".to_string(),
));
}
_ => {}
}
}
Ok(())
}
fn tag() -> Tag {
Tag::DataProcessingList
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
writer.write_arc_list("dataProcessingList", &self.list)
}
}
#[derive(Debug)]
pub struct DataProcessing {
id: String,
processing_methods: Vec<ProcessingMethod>,
}
impl Clone for DataProcessing {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
processing_methods: self.processing_methods.clone(),
}
}
}
impl DataProcessing {
pub fn new(id: &str) -> Self {
DataProcessing {
id: id.into(),
processing_methods: Vec::new(),
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn processing_methods(&self) -> &[ProcessingMethod] {
&self.processing_methods
}
}
impl MzMLTag for DataProcessing {
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().local_name().as_ref() != b"dataProcessing" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing DataProcessing",
start_event,
)))
} else {
let attributes =
parser.process_attributes(Tag::DataProcessing, &ID_ATTRIBUTE, start_event)?;
let data_processing = match attributes.get("id") {
Some(AttributeValue::String(id)) => {
DataProcessing::new(parser.parse_string(Tag::DataProcessing, id).unwrap_or(""))
}
_ => DataProcessing::new(""),
};
parser
.breadcrumbs
.push_back((Tag::DataProcessing, Some(data_processing.id().to_owned())));
Ok(Some(data_processing))
}
}
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"processingMethod" => {
if let Some(mut processing_method) =
ProcessingMethod::parse_start_tag(parser, &start_event)?
{
processing_method.parse_xml(parser, buffer)?;
self.processing_methods.push(processing_method);
}
}
_ => 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"dataProcessing" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag(
"dataProcessing".to_string(),
));
}
_ => {}
}
}
Ok(())
}
fn tag() -> Tag {
Tag::DataProcessing
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
writer.start_tag_with_attr("dataProcessing", "id", &self.id)?;
for processing_method in &self.processing_methods {
processing_method.write_xml(writer)?;
}
writer.end_tag("dataProcessing")
}
}
#[derive(Debug)]
pub struct ProcessingMethod {
order: u16,
software_ref: SoftwareRef,
param_group_refs: Vec<ReferenceableParamGroupRef>,
cv_params: Vec<CVParam>,
user_params: Vec<UserParam>,
}
impl Clone for ProcessingMethod {
fn clone(&self) -> Self {
Self {
order: self.order,
software_ref: self.software_ref.clone(),
param_group_refs: self.param_group_refs.clone(),
cv_params: self.cv_params.clone(),
user_params: self.user_params.clone(),
}
}
}
impl ProcessingMethod {
pub fn new(order: u16, software_ref: SoftwareRef) -> Self {
ProcessingMethod {
order,
software_ref,
param_group_refs: Vec::new(),
cv_params: Vec::new(),
user_params: Vec::new(),
}
}
pub fn order(&self) -> u16 {
self.order
}
}
impl MzMLTag for ProcessingMethod {
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().local_name().as_ref() != b"processingMethod" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing ProcessingMethod",
start_event,
)))
} else {
let attributes = parser.process_attributes(
Tag::ProcessingMethod,
&PROCESSING_METHOD_ATTRIBUTES,
start_event,
)?;
let processing_method = match (attributes.get("order"), attributes.get("softwareRef")) {
(
Some(AttributeValue::Integer(order)),
Some(AttributeValue::String(software_ref)),
) => {
let software_ref_id = parser
.parse_string(Tag::ProcessingMethod, software_ref)
.unwrap_or("");
let software_ref = match parser.software_ref(software_ref_id.as_bytes()) {
Some(software_ref) => software_ref,
None => {
parser.errors.push_back(ParseError::MissingRef {
breadcrumbs: Breadcrumbs(parser.breadcrumbs.clone()),
tag: Tag::ProcessingMethod,
ref_to_tag: Tag::Software,
ref_id: software_ref_id.to_string(),
});
SoftwareRef::Id(software_ref_id.to_string())
}
};
ProcessingMethod::new(*order as u16, software_ref)
}
(Some(AttributeValue::Integer(order)), None) => {
ProcessingMethod::new(*order as u16, SoftwareRef::Id("".to_string()))
}
(None, Some(AttributeValue::String(software_ref))) => {
let software_ref = parser
.parse_string(Tag::ProcessingMethod, software_ref)
.unwrap_or("");
let software_ref = parser.software_ref(software_ref.as_bytes()).unwrap();
ProcessingMethod::new(0, software_ref)
}
_ => ProcessingMethod::new(0, SoftwareRef::Id("".to_string())),
};
parser.breadcrumbs.push_back((Tag::ProcessingMethod, None));
Ok(Some(processing_method))
}
}
fn parse_xml<B: BufRead>(
&mut self,
parser: &mut MzMLReader<B>,
buffer: &mut Vec<u8>,
) -> Result<(), FatalParseError> {
loop {
buffer.clear();
match parser.next(buffer)? {
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);
}
}
_ => 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"processingMethod" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag(
"processingMethod".to_string(),
));
}
_ => {}
}
}
Ok(())
}
fn tag() -> Tag {
Tag::ProcessingMethod
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
let mut elem = BytesStart::new("processingMethod");
elem.push_attribute(("order", self.order().to_string().as_str()));
elem.push_attribute(("softwareRef", self.software_ref.id()));
writer.write_event(Event::Start(elem))?;
self.write_ref_param_groups_xml(writer)?;
self.write_params_xml(writer)?;
writer.end_tag("processingMethod")
}
}
impl HasCVParams for ProcessingMethod {
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 ProcessingMethod {
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
}
}