use crate::{
mzml::{
attributes::{AttributeValue, LIST_ATTRIBUTES},
cvparam::UserParam,
},
FatalParseError, ParseError, Representation, Tag, ACCESSION_CENTROID_SPECTRUM,
ACCESSION_PROFILE_SPECTRUM,
};
use super::{
cvparam::{CVParam, HasCVParams, HasParamGroupRefs},
referenceableparamgroup::ReferenceableParamGroupRef,
writer::Writer,
MzMLReader, MzMLTag,
};
use quick_xml::events::{BytesStart, Event};
use std::{
borrow::Cow,
io::{BufRead, Write},
sync::Arc,
};
pub const ACCESSION_SHA_256: &str = "MS:1003151";
pub const ACCESSION_IBD_SHA_256: &str = "IMS:1000092";
pub const ACCESSION_IBD_PROCESSED: &str = "IMS:1000031";
pub const ACCESSION_IBD_CONTINUOUS: &str = "IMS:1000030";
pub const ACCESSION_IBD_UUID: &str = "IMS:1000080";
pub struct FileDescription {
pub(super) file_content: FileContent,
pub(crate) source_file_list: Vec<Arc<SourceFile>>,
pub(crate) contact_list: Vec<Contact>,
}
impl Clone for FileDescription {
fn clone(&self) -> Self {
Self {
file_content: self.file_content.clone(),
source_file_list: self.source_file_list.clone(),
contact_list: self.contact_list.clone(),
}
}
}
impl Default for FileDescription {
fn default() -> Self {
Self::new()
}
}
impl FileDescription {
pub fn new() -> Self {
FileDescription {
file_content: FileContent::new(),
source_file_list: Vec::new(),
contact_list: Vec::new(),
}
}
pub fn add_source_file(&mut self, source_file: SourceFile) {
self.source_file_list.push(Arc::new(source_file));
}
pub fn add_contact(&mut self, contact: Contact) {
self.contact_list.push(contact);
}
pub fn file_content(&self) -> &FileContent {
&self.file_content
}
pub fn file_content_mut(&mut self) -> &mut FileContent {
&mut self.file_content
}
}
impl MzMLTag for FileDescription {
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"fileDescription" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing FileDescription",
start_event,
)))
} else {
parser.breadcrumbs.push_back((Tag::FileDescription, None));
Ok(Some(FileDescription::new()))
}
}
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"fileContent" => {
if let Some(mut file_content) =
FileContent::parse_start_tag(parser, &start_event)?
{
file_content.parse_xml(parser, buffer)?;
self.file_content = file_content;
}
}
b"sourceFileList" => {
if let Some(mut source_file_list) =
SourceFileList::parse_start_tag(parser, &start_event)?
{
source_file_list.parse_xml(parser, buffer)?;
self.source_file_list = source_file_list.list;
}
}
b"contact" => {
if let Some(mut contact) =
Contact::parse_start_tag(parser, &start_event)?
{
contact.parse_xml(parser, buffer)?;
self.contact_list.push(contact);
}
}
_ => 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"fileDescription" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag(
"fileDescription".to_string(),
));
}
_ => {}
}
}
Ok(())
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
writer.start_tag("fileDescription")?;
self.file_content.write_xml(writer)?;
writer.write_arc_list("sourceFileList", &self.source_file_list)?;
for contact in &self.contact_list {
contact.write_xml(writer)?;
}
writer.end_tag("fileDescription")
}
fn tag() -> Tag {
Tag::FileDescription
}
}
pub struct FileContent {
param_group_refs: Vec<ReferenceableParamGroupRef>,
cv_params: Vec<CVParam>,
user_params: Vec<UserParam>,
}
impl Clone for FileContent {
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(),
}
}
}
impl Default for FileContent {
fn default() -> Self {
Self::new()
}
}
impl FileContent {
pub fn new() -> Self {
FileContent {
param_group_refs: Vec::new(),
cv_params: Vec::new(),
user_params: Vec::new(),
}
}
}
impl MzMLTag for FileContent {
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"fileContent" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing FileContent",
start_event,
)))
} else {
parser.breadcrumbs.push_back((Tag::FileContent, None));
Ok(Some(FileContent::new()))
}
}
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)?
{
match cv_param.accession() {
ACCESSION_CENTROID_SPECTRUM => {
parser.representation = Some(Representation::Centroid)
}
ACCESSION_PROFILE_SPECTRUM => {
parser.representation = Some(Representation::Profile)
}
_ => {}
}
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"fileContent" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag(
"fileContent".to_string(),
));
}
_ => {}
}
}
Ok(())
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
writer.start_tag("fileContent")?;
self.write_ref_param_groups_xml(writer)?;
self.write_params_xml(writer)?;
writer.end_tag("fileContent")
}
fn tag() -> Tag {
Tag::FileContent
}
}
impl HasCVParams for FileContent {
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 FileContent {
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
}
}
pub struct SourceFileList {
pub(crate) list: Vec<Arc<SourceFile>>,
}
impl SourceFileList {
fn new(count: usize) -> Self {
SourceFileList {
list: Vec::with_capacity(count),
}
}
}
impl MzMLTag for SourceFileList {
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"sourceFileList" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing SourceFileList",
start_event,
)))
} else {
let attributes =
parser.process_attributes(Self::tag(), &LIST_ATTRIBUTES, start_event)?;
let count = match attributes.get("count") {
Some(&AttributeValue::Integer(count)) => count as usize,
_ => 0,
};
parser.breadcrumbs.push_back((Tag::SourceFileList, None));
Ok(Some(SourceFileList::new(count)))
}
}
fn parse_xml<B: BufRead>(
&mut self,
parser: &mut MzMLReader<B>,
buffer: &mut Vec<u8>,
) -> Result<(), FatalParseError> {
let mut last_num_params = 0;
loop {
buffer.clear();
let next_event = parser.next(buffer)?;
let is_empty = matches!(next_event, Event::Empty(_));
match next_event {
Event::Start(start_event) | Event::Empty(start_event) => {
match start_event.name().as_ref() {
b"sourceFile" => {
if let Some(mut source_file) =
SourceFile::parse_start_tag(parser, &start_event)?
{
if !is_empty {
source_file.cv_params.reserve(last_num_params);
source_file.parse_xml(parser, buffer)?;
last_num_params = source_file.cv_params.len();
}
self.list.push(Arc::new(source_file));
}
}
_ => 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"sourceFileList" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag(
"sourceFileList".to_string(),
));
}
_ => {}
}
}
Ok(())
}
fn tag() -> Tag {
Tag::SourceFileList
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
writer.write_arc_list("sourceFileList", &self.list)
}
}
#[derive(Debug)]
pub enum SourceFileRef {
Id(String),
Ref(Arc<SourceFile>),
}
impl Clone for SourceFileRef {
fn clone(&self) -> Self {
match self {
Self::Id(arg0) => Self::Id(arg0.clone()),
Self::Ref(arg0) => Self::Ref(arg0.clone()),
}
}
}
#[derive(Debug)]
pub struct SourceFile {
id: Arc<str>,
name: String,
location: String,
param_group_refs: Vec<ReferenceableParamGroupRef>,
cv_params: Vec<CVParam>,
user_params: Vec<UserParam>,
}
impl SourceFile {
pub fn new(id: &str, name: &str, location: &str) -> Self {
SourceFile {
id: id.into(),
name: name.into(),
location: location.into(),
param_group_refs: Vec::new(),
cv_params: Vec::new(),
user_params: Vec::new(),
}
}
pub fn id(&self) -> &str {
&self.id
}
}
impl MzMLTag for SourceFile {
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"sourceFile" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing SourceFile",
start_event,
)))
} else {
let mut id: Option<Cow<[u8]>> = None;
let mut name: Option<Cow<[u8]>> = None;
let mut location: 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.to_owned());
}
b"name" => {
name = Some(attribute.value.to_owned());
}
b"location" => {
location = Some(attribute.value.to_owned());
}
_ => {
parser.errors.push_back(ParseError::UnexpectedAttribute((
Tag::SourceFile,
std::str::from_utf8(attribute.key.as_ref())?.to_string(),
)));
}
},
Err(error) => {
parser
.errors
.push_back(ParseError::XMLError((Tag::SourceFile, error.into())));
}
};
}
let id = match &id {
Some(id) => parser.parse_string(Tag::SourceFile, id).unwrap(),
None => {
parser.errors.push_back(ParseError::MissingAttribute((
Tag::SourceFile,
"id".to_string(),
)));
""
}
};
let name = match &name {
Some(name) => parser.parse_string(Tag::SourceFile, name).unwrap(),
None => {
parser.errors.push_back(ParseError::MissingAttribute((
Tag::SourceFile,
"name".to_string(),
)));
""
}
};
let location = match &location {
Some(location) => parser.parse_string(Tag::SourceFile, location).unwrap(),
None => {
parser.errors.push_back(ParseError::MissingAttribute((
Tag::SourceFile,
"location".to_string(),
)));
""
}
};
parser
.breadcrumbs
.push_back((Tag::SourceFile, Some(id.to_owned())));
Ok(Some(SourceFile::new(id, name, location)))
}
}
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"sourceFile" = end_event.name().local_name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag("sourceFile".to_string()));
}
_ => {}
}
}
Ok(())
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
let mut elem = BytesStart::new("sourceFile");
elem.push_attribute(("id", self.id.as_ref()));
elem.push_attribute(("location", self.location.as_str()));
elem.push_attribute(("name", self.name.as_str()));
writer.write_event(Event::Start(elem))?;
self.write_ref_param_groups_xml(writer)?;
self.write_params_xml(writer)?;
writer.end_tag("sourceFile")
}
fn tag() -> Tag {
Tag::SourceFile
}
}
impl HasCVParams for SourceFile {
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 SourceFile {
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
}
}
pub struct Contact {
param_group_refs: Vec<ReferenceableParamGroupRef>,
cv_params: Vec<CVParam>,
user_params: Vec<UserParam>,
}
impl Clone for Contact {
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(),
}
}
}
impl Contact {
pub fn new() -> Contact {
Contact {
param_group_refs: Vec::new(),
cv_params: Vec::new(),
user_params: Vec::new(),
}
}
}
impl Default for Contact {
fn default() -> Self {
Self::new()
}
}
impl MzMLTag for Contact {
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"contact" {
Err(FatalParseError::UnexpectedTag(format!(
"Unexpected event {:?} when processing Contact",
start_event,
)))
} else {
parser.breadcrumbs.push_back((Tag::Contact, None));
Ok(Some(Contact::new()))
}
}
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"contact" = end_event.name().as_ref() {
parser.breadcrumbs.pop_back();
break;
}
}
Event::Eof => {
return Err(FatalParseError::MissingClosingTag("contact".to_string()));
}
_ => {}
}
}
Ok(())
}
fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
writer.start_tag("contact")?;
self.write_ref_param_groups_xml(writer)?;
self.write_params_xml(writer)?;
writer.end_tag("contact")
}
fn tag() -> Tag {
Tag::Contact
}
}
impl HasCVParams for Contact {
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 Contact {
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
}
}