use std::{
borrow::Cow,
io::{BufRead, Write},
};
use quick_xml::events::{BytesStart, Event};
use crate::{
mzml::attributes::{AttributeValue, LIST_ATTRIBUTES},
FatalParseError, ParseError, Tag,
};
use super::{writer::Writer, MzMLReader, MzMLTag};
pub struct CVList {
pub(crate) cvs: Vec<CV>,
}
impl CVList {
fn new(count: usize) -> Self {
CVList {
cvs: Vec::with_capacity(count),
}
}
}
impl MzMLTag for CVList {
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"cvList" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing CVList",
start_event,
)))
} else {
let attributes =
parser.process_attributes(Tag::CVList, &LIST_ATTRIBUTES, start_event)?;
let count = match attributes.get("count") {
Some(&AttributeValue::Integer(count)) => count as usize,
_ => 0,
};
parser.breadcrumbs.push_back((Tag::CVList, None));
Ok(Some(CVList::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"cv" => {
if let Some(cv) = CV::parse_start_tag(parser, &start_event)? {
self.cvs.push(cv);
}
}
_ => parser.errors.push_back(ParseError::UnexpectedTag(format!(
"{:?} unexpected when processing {:?}",
std::str::from_utf8(start_event.name().as_ref()),
Tag::CVList
))),
}
}
Event::End(end_event) => {
if let b"cvList" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag("cvList".to_string()));
}
_ => {}
}
}
Ok(())
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
writer.write_list("cvList", &self.cvs)
}
#[inline]
fn tag() -> Tag {
Tag::CVList
}
}
pub struct CV {
uri: String,
full_name: String,
id: String,
version: Option<String>,
}
impl Clone for CV {
fn clone(&self) -> Self {
Self {
uri: self.uri.clone(),
full_name: self.full_name.clone(),
id: self.id.clone(),
version: self.version.clone(),
}
}
}
impl CV {
pub fn new(id: &str, uri: &str, full_name: &str) -> CV {
CV {
id: id.into(),
uri: uri.into(),
full_name: full_name.into(),
version: None,
}
}
#[inline]
pub fn set_version(&mut self, version: &str) {
self.version = Some(version.into());
}
}
impl MzMLTag for CV {
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"cv" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing CV",
start_event,
)))
} else {
let mut id: Option<Cow<[u8]>> = None;
let mut uri: Option<Cow<[u8]>> = None;
let mut full_name: Option<Cow<[u8]>> = None;
let mut version: Option<Cow<[u8]>> = None;
for attribute in start_event
.attributes()
.with_checks(parser.with_attribute_checks)
{
match attribute {
Ok(attribute) => match attribute.key.as_ref() {
b"id" => {
id = Some(attribute.value);
}
b"URI" | b"uri" => {
uri = Some(attribute.value);
}
b"fullName" => {
full_name = Some(attribute.value);
}
b"version" => {
version = Some(attribute.value);
}
_ => {
parser.errors.push_back(ParseError::UnexpectedAttribute((
Tag::CV,
std::str::from_utf8(attribute.key.as_ref())?.to_string(),
)));
}
},
Err(error) => {
parser
.errors
.push_back(ParseError::XMLError((Tag::CV, error.into())));
}
};
}
let id = match &id {
Some(id) => parser.parse_string(Tag::CV, id).unwrap_or(""),
None => {
parser
.errors
.push_back(ParseError::MissingAttribute((Tag::CV, "id".to_string())));
""
}
};
let uri = match &uri {
Some(uri) => parser.parse_string(Tag::CV, uri).unwrap_or(""),
None => {
parser
.errors
.push_back(ParseError::MissingAttribute((Tag::CV, "uri".to_string())));
""
}
};
let full_name = match &full_name {
Some(full_name) => parser.parse_string(Tag::CV, full_name).unwrap_or(""),
None => {
parser.errors.push_back(ParseError::MissingAttribute((
Tag::CV,
"full_name".to_string(),
)));
""
}
};
let mut cv = CV::new(id, uri, full_name);
if let Some(version) = version {
cv.set_version(parser.parse_string(Tag::CV, &version).unwrap_or(""));
}
Ok(Some(cv))
}
}
fn parse_xml<B: BufRead>(
&mut self,
_parser: &mut MzMLReader<B>,
_buffer: &mut Vec<u8>,
) -> Result<(), FatalParseError> {
Ok(())
}
fn tag() -> Tag {
Tag::CV
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
let mut elem = BytesStart::new("cv");
elem.push_attribute(("uri", self.uri.as_ref()));
elem.push_attribute(("fullName", self.full_name.as_ref()));
elem.push_attribute(("id", self.id.as_ref()));
if let Some(ref version) = self.version {
elem.push_attribute(("version", version.as_ref()));
}
writer.write_event(Event::Empty(elem))
}
}