use crate::error::Breadcrumbs;
use crate::mzml::attributes::{AttributeValue, ID_ATTRIBUTE, LIST_ATTRIBUTES};
use crate::mzml::cvparam::UserParam;
use crate::{FatalParseError, ParseError, Tag};
use super::cvparam::{CVParam, HasCVParams};
use super::writer::Writer;
use super::{MzMLReader, MzMLTag};
use quick_xml::events::{BytesStart, Event};
use std::io::{BufRead, Write};
use std::sync::Arc;
use std::borrow::Borrow;
#[derive(Debug)]
pub enum ReferenceableParamGroupRef {
Id(String),
Ref(Arc<ReferenceableParamGroup>),
}
impl Clone for ReferenceableParamGroupRef {
fn clone(&self) -> Self {
match self {
Self::Id(arg0) => Self::Id(arg0.clone()),
Self::Ref(arg0) => Self::Ref(arg0.clone()),
}
}
}
impl AsRef<ReferenceableParamGroup> for ReferenceableParamGroupRef {
fn as_ref(&self) -> &ReferenceableParamGroup {
match self {
ReferenceableParamGroupRef::Ref(param_group) => param_group.as_ref(),
ReferenceableParamGroupRef::Id(id) => {
panic!("Can't dereference an ID based param group: {}", id)
}
}
}
}
impl ReferenceableParamGroupRef {
pub fn parse_start_tag<B: BufRead>(
parser: &mut MzMLReader<B>,
start_event: &BytesStart,
) -> Result<Self, FatalParseError>
where
Self: std::marker::Sized,
{
if start_event.name().as_ref() != b"referenceableParamGroupRef" {
panic!(
"Unexpected event {:?} when processing ReferenceableParamGroupRef",
start_event
);
}
let mut value: Option<String> = None;
for attribute in start_event
.attributes()
.with_checks(parser.with_attribute_checks)
{
match attribute {
Ok(attribute) => match attribute.key.as_ref() {
b"ref" => match std::str::from_utf8(attribute.value.borrow()) {
Ok(v) => {
value = Some(v.to_owned());
}
Err(error) => {
parser
.errors
.push_back(ParseError::Utf8Error((Tag::RefParamGroupRef, error)));
}
},
_ => parser.errors.push_back(ParseError::UnexpectedAttribute((
Tag::RefParamGroupRef,
std::str::from_utf8(attribute.key.as_ref())?.to_string(),
))),
},
Err(error) => {
parser
.errors
.push_back(ParseError::XMLError((Tag::RefParamGroupRef, error.into())));
}
}
}
match value {
Some(value) => match parser.referenceable_param_group_ref(value.as_bytes()) {
Some(reference) => Ok(reference),
None => {
parser.errors.push_back(ParseError::MissingRef {
breadcrumbs: Breadcrumbs(parser.breadcrumbs.clone()),
tag: *parser.last_tag(),
ref_to_tag: Tag::RefParamGroup,
ref_id: value.to_string(),
});
Ok(ReferenceableParamGroupRef::Id(value))
}
},
None => todo!(),
}
}
}
pub struct ReferenceableParamGroupList {
pub(crate) list: Vec<Arc<ReferenceableParamGroup>>,
}
impl ReferenceableParamGroupList {
fn new(count: usize) -> Self {
Self {
list: Vec::with_capacity(count),
}
}
}
impl MzMLTag for ReferenceableParamGroupList {
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"referenceableParamGroupList" {
return Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing ReferenceableParamGroupList",
start_event,
)));
}
let attributes =
parser.process_attributes(Tag::RefParamGroupList, &LIST_ATTRIBUTES, start_event)?;
let count = match attributes.get("count") {
Some(&AttributeValue::Integer(count)) => count as usize,
_ => 0,
};
parser.breadcrumbs.push_back((Tag::RefParamGroupList, None));
Ok(Some(ReferenceableParamGroupList::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"referenceableParamGroup" => {
if let Some(mut param_group) =
ReferenceableParamGroup::parse_start_tag(parser, &start_event)?
{
param_group.parse_xml(parser, buffer)?;
self.list.push(Arc::new(param_group));
}
}
_ => 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"referenceableParamGroupList" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag(
"referenceableParamGroupList".to_string(),
));
}
_ => {}
}
}
Ok(())
}
fn tag() -> Tag {
Tag::RefParamGroupList
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
writer.write_arc_list("referenceableParamGroupList", &self.list)
}
}
#[derive(Debug)]
pub struct ReferenceableParamGroup {
id: String,
cv_params: Vec<CVParam>,
user_params: Vec<UserParam>,
}
impl Clone for ReferenceableParamGroup {
fn clone(&self) -> Self {
Self {
id: self.id.clone(),
cv_params: self.cv_params.clone(),
user_params: self.user_params.clone(),
}
}
}
impl ReferenceableParamGroup {
pub fn new(id: &str) -> Self {
ReferenceableParamGroup {
id: id.into(),
cv_params: Vec::new(),
user_params: Vec::new(),
}
}
pub fn id(&self) -> &str {
&self.id
}
}
impl MzMLTag for ReferenceableParamGroup {
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"referenceableParamGroup" {
return Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing ReferenceableParamGroup",
start_event,
)));
}
let attributes =
parser.process_attributes(Tag::RefParamGroup, &ID_ATTRIBUTE, start_event)?;
let id = match attributes.get("id") {
Some(AttributeValue::String(id)) => {
parser.parse_string(Tag::RefParamGroup, id).unwrap_or("")
}
_ => todo!(),
};
parser
.breadcrumbs
.push_back((Tag::RefParamGroup, Some(id.to_owned())));
Ok(Some(ReferenceableParamGroup::new(id)))
}
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"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"referenceableParamGroup" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag(
"referenceableParamGroup".to_string(),
));
}
_ => {}
}
}
Ok(())
}
fn tag() -> Tag {
Tag::RefParamGroup
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
writer.start_tag_with_attr("referenceableParamGroup", "id", self.id())?;
self.write_params_xml(writer)?;
writer.end_tag("referenceableParamGroup")
}
}
impl HasCVParams for ReferenceableParamGroup {
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
}
}