1use std::borrow::{Borrow, Cow};
2use std::collections::VecDeque;
3
4use quick_xml::events::{BytesStart, Event};
5use quick_xml::Decoder;
6use std::io::{BufRead, Write};
7
8use crate::error::Breadcrumbs;
9use crate::obo::{OBOTerm, Ontology, Term, ValueType};
10use crate::{FatalParseError, ParseError, Tag};
11
12use super::referenceableparamgroup::ReferenceableParamGroupRef;
13use super::writer::Writer;
14use super::{MzMLReader, MzMLTag};
15
16pub fn search_all_params<'a, T: HasCVParams + HasParamGroupRefs>(
18 tag: &'a T,
19 accession: &str,
20) -> Option<&'a CVParam> {
21 match tag.search_param_groups(accession) {
22 Some(cv_param) => Some(cv_param),
23 None => match tag.search_cv_params(accession) {
24 Some(cv_param) => Some(cv_param),
25 None => None,
26 },
27 }
28}
29
30pub fn children_of<'a, T: HasCVParams + HasParamGroupRefs>(
32 ontology: &'a Ontology,
33 tag: &'a T,
34 accession: &str,
35) -> Vec<&'a CVParam> {
36 let mut children = tag.children_of(ontology, accession);
37
38 for ref_group in tag.param_group_refs() {
39 children.extend(ref_group.as_ref().children_of(ontology, accession).iter());
40 }
41
42 children
43}
44
45pub trait HasCVParams {
47 fn add_cv_param(&mut self, param: CVParam);
58 fn cv_params(&self) -> &Vec<CVParam>;
60 fn cv_params_mut(&mut self) -> &mut Vec<CVParam>;
62
63 fn clone_params(&self) -> Vec<CVParam> {
65 let mut params = Vec::new();
66
67 for param in self.cv_params() {
68 params.push(param.clone())
69 }
70
71 params
72 }
73
74 fn remove_children_of(&mut self, ontology: &Ontology, parent_id: &str) {
76 let cv_params = self.cv_params_mut();
77
78 cv_params.retain(|cv_param| {
79 if let Some(term) = cv_param.term() {
80 !term.is_child_of(ontology, parent_id)
81 } else {
82 true
83 }
84 })
85 }
86
87 fn children_of(&self, ontology: &Ontology, parent_id: &str) -> Vec<&CVParam> {
89 let mut children = Vec::new();
90
91 for cv_param in self.cv_params() {
92 if let Some(term) = cv_param.term() {
93 if term.is_child_of(ontology, parent_id) {
94 children.push(cv_param);
95 }
96 }
97 }
98
99 children
100 }
101
102 fn add_user_param(&mut self, param: UserParam);
104 fn user_params(&self) -> &Vec<UserParam>;
106
107 fn search_cv_params(&self, accession: &str) -> Option<&CVParam> {
109 self.cv_params()
110 .iter()
111 .find(|¶m| param.term.id() == accession)
112 }
113
114 fn write_params_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
116 for param in self.cv_params() {
117 param.write_xml(writer)?;
118 }
119
120 for param in self.user_params() {
121 param.write_xml(writer)?;
122 }
123
124 Ok(())
125 }
126}
127
128pub trait HasParamGroupRefs: HasCVParams {
130 fn clone_params_with_groups(&self) -> Vec<CVParam> {
132 let mut params = Vec::new();
133
134 for group_ref in self.param_group_refs() {
135 match group_ref {
136 ReferenceableParamGroupRef::Id(_) => todo!(),
137 ReferenceableParamGroupRef::Ref(group_ref) => {
138 for param in group_ref.cv_params() {
139 params.push(param.clone());
140 }
141 }
142 }
143 }
144
145 for param in self.cv_params() {
146 params.push(param.clone())
147 }
148
149 params
150 }
151
152 fn add_param_group_ref(&mut self, param_group_ref: ReferenceableParamGroupRef);
154
155 fn param_group_refs(&self) -> &Vec<ReferenceableParamGroupRef>;
157
158 fn search_param_groups(&self, accession: &str) -> Option<&CVParam> {
160 for ref_group in self.param_group_refs() {
161 if let ReferenceableParamGroupRef::Ref(ref_group) = ref_group {
162 if let Some(cv_param) = ref_group.search_cv_params(accession) {
163 return Some(cv_param);
164 }
165 }
166 }
167 None
168 }
169
170 fn write_ref_param_groups_xml<W: Write>(
172 &self,
173 writer: &mut Writer<W>,
174 ) -> Result<(), quick_xml::Error> {
175 for ref_group in self.param_group_refs() {
176 let mut elem = BytesStart::new("referenceableParamGroupRef");
177
178 match ref_group {
179 ReferenceableParamGroupRef::Id(id) => {
180 elem.push_attribute(("ref", id.as_str()));
181 }
182 ReferenceableParamGroupRef::Ref(group_ref) => {
183 elem.push_attribute(("ref", group_ref.id()));
184 }
185 }
186
187 writer.write_event(Event::Empty(elem))?;
188 }
189
190 Ok(())
191 }
192}
193
194#[derive(Debug)]
196pub enum CVParamValue {
197 Empty,
199 Boolean(bool),
201 String(String),
203 Float(f64),
206 Integer(i64),
208 NonNegativeInteger(u64),
210 DateTime(String),
213}
214
215impl Clone for CVParamValue {
216 fn clone(&self) -> Self {
217 match self {
218 Self::Empty => Self::Empty,
219 Self::Boolean(arg0) => Self::Boolean(*arg0),
220 Self::String(arg0) => Self::String(arg0.clone()),
221 Self::Float(arg0) => Self::Float(*arg0),
222 Self::Integer(arg0) => Self::Integer(*arg0),
223 Self::NonNegativeInteger(arg0) => Self::NonNegativeInteger(*arg0),
224 Self::DateTime(arg0) => Self::DateTime(arg0.clone()),
225 }
226 }
227}
228
229impl CVParamValue {
230 pub fn is_integer(&self) -> bool {
232 matches!(self, CVParamValue::Integer(_))
233 }
234
235 pub fn as_u32(&self) -> Option<u32> {
237 match self {
238 CVParamValue::Integer(value) => Some(*value as u32),
239 CVParamValue::NonNegativeInteger(value) => Some(*value as u32),
240 _ => None,
241 }
242 }
243
244 pub fn as_u64(&self) -> Option<u64> {
246 match self {
247 CVParamValue::Integer(value) => Some(*value as u64),
248 CVParamValue::NonNegativeInteger(value) => Some(*value),
249 _ => None,
250 }
251 }
252
253 pub fn as_f64(&self) -> Option<f64> {
255 match self {
256 CVParamValue::Float(value) => Some(*value),
257 _ => None,
258 }
259 }
260}
261
262pub struct RawTerm<'a> {
264 decoder: Decoder,
266 ontology: Ontology,
267 accession: Cow<'a, [u8]>,
270 name: Cow<'a, [u8]>,
271 value: Option<Cow<'a, [u8]>>,
272 unit_accession: Option<Cow<'a, [u8]>>,
273 unit_name: Option<Cow<'a, [u8]>>,
274}
275
276impl<'a> RawTerm<'a> {
277 pub(crate) fn raw_accession(&self) -> &[u8] {
278 &self.accession
279 }
280
281 #[inline]
282 pub(crate) fn accession(&self) -> Cow<str> {
283 self.decoder.decode(&self.accession).unwrap()
284 }
291
292 #[inline]
293 pub(crate) fn name(&self) -> Cow<str> {
294 self.decoder.decode(&self.name).unwrap()
295 }
296
297 #[inline]
298 pub(crate) fn value_as_f64(&self) -> f64 {
299 self.decoder
300 .decode(self.value.as_ref().unwrap().borrow())
301 .unwrap()
302 .parse()
303 .unwrap()
304 }
305
306 #[inline]
307 pub(crate) fn value_as_u64(&self) -> u64 {
308 self.decoder
309 .decode(self.value.as_ref().unwrap().borrow())
310 .unwrap()
311 .parse()
312 .unwrap()
313 }
314
315 #[inline]
316 pub(crate) fn value_as_u32(&self) -> u32 {
317 self.decoder
318 .decode(self.value.as_ref().unwrap().borrow())
319 .unwrap()
320 .parse()
321 .unwrap()
322 }
323
324 pub(crate) fn parse_start_tag<'b, B: BufRead>(
325 parser: &'b mut MzMLReader<B>,
326 start_event: &'a BytesStart,
327 ) -> Result<Self, FatalParseError>
328 where
329 Self: std::marker::Sized,
330 {
331 let mut accession: Option<Cow<[u8]>> = None;
332 let mut name: Option<Cow<[u8]>> = None;
333 let mut value: Option<Cow<[u8]>> = None;
334
335 let mut unit_accession: Option<Cow<[u8]>> = None;
336 let mut unit_name: Option<Cow<[u8]>> = None;
337
338 for att in start_event
339 .attributes()
340 .with_checks(parser.with_attribute_checks)
341 {
342 match att {
343 Ok(att) => {
344 match att.key.as_ref() {
345 b"cvRef" => {}
347 b"accession" => {
348 accession = Some(att.value);
349 }
350 b"name" => {
351 name = Some(att.value);
352 }
353 b"value" => {
354 value = Some(att.value);
355 }
356 b"unitCvRef" => {}
358 b"unitAccession" => {
359 unit_accession = Some(att.value);
360 }
361 b"unitName" => {
362 unit_name = Some(att.value);
363 }
364 _ => {
365 parser.errors.push_back(ParseError::UnexpectedAttribute((
366 Tag::CVParam,
367 std::str::from_utf8(att.key.as_ref())?.to_string(),
368 )));
369 }
370 }
371 }
372 Err(error) => {
373 parser
374 .errors
375 .push_back(ParseError::XMLError((Tag::CVParam, error.into())));
376 }
377 };
378 }
379
380 match (accession, name) {
381 (Some(accession), Some(name)) => Ok(RawTerm {
382 ontology: parser.ontology.clone(),
383 decoder: parser.decoder,
384 accession,
385 name,
386 value,
387 unit_accession,
388 unit_name,
389 }),
390 (accession, name) => Err(FatalParseError::InvalidTag((
391 Tag::CVParam,
392 format!(
393 "Missing asscession ({:?}) or name ({:?}) when parsing a RawTerm: {}",
394 accession,
395 name,
396 parser
397 .decoder
398 .decode(start_event.name().local_name().as_ref())
399 .unwrap()
400 ),
401 ))),
402 }
403 }
404
405 pub fn to_user_param(self) -> UserParam {
407 let name = self.decoder.decode(&self.name).unwrap();
408 let value = match &self.value {
409 Some(value) => Some(self.decoder.decode(value).unwrap().into_owned()),
410 None => None,
411 };
412
413 let unit_term = match (&self.unit_accession, &self.unit_name) {
414 (Some(unit_term), Some(unit_name)) => Some(Accession::String {
415 accession: self.decoder.decode(unit_term).unwrap().into_owned(),
416 name: self.decoder.decode(unit_name).unwrap().into_owned(),
417 }),
418 (Some(unit_term), None) => Some(Accession::String {
419 accession: self.decoder.decode(unit_term).unwrap().into_owned(),
420 name: "".to_owned(),
421 }),
422 (None, Some(unit_name)) => Some(Accession::String {
423 accession: "".to_owned(),
424 name: self.decoder.decode(unit_name).unwrap().into_owned(),
425 }),
426 (None, None) => None,
427 };
428
429 UserParam {
430 name: name.into_owned(),
431 data_type: None,
432 value,
433 unit_term,
434 }
435 }
436
437 pub fn to_cv_param(
439 &self,
440 breadcrumbs: &VecDeque<(Tag, Option<String>)>,
441 errors: &mut VecDeque<ParseError>,
442 ) -> CVParam {
443 let term = match self.ontology.get(&self.accession()) {
444 Some(term) => Accession::Term(term.clone()),
445 None => {
446 errors.push_back(ParseError::UnknownAccession((
447 Breadcrumbs(breadcrumbs.to_owned()),
448 self.accession().to_string(),
449 )));
450
451 Accession::String {
452 accession: self.accession().to_string(),
453 name: self.name().to_string(),
454 }
455 }
457 };
458
459 let mut cv_param;
460
461 match &self.value {
462 Some(value) => {
463 let value = self.decoder.decode(value).unwrap();
464
465 if value.is_empty() {
466 if let Some(value_type) = term.value_type() {
467 errors.push_back(ParseError::MissingValue((
468 Breadcrumbs(breadcrumbs.to_owned()),
469 format!("[{}] {}", term.id(), term.name()),
470 value_type,
471 )));
472 }
473
474 cv_param = CVParam::new(term, CVParamValue::Empty);
475 } else {
476 let param_value = match term.value_type() {
477 Some(ValueType::String) => CVParamValue::String(value.to_string()),
478 Some(ValueType::Boolean) => match value.as_ref() {
479 "true" => CVParamValue::Boolean(true),
480 "false" => CVParamValue::Boolean(false),
481 _ => {
482 errors.push_back(ParseError::InvalidValue((
483 Breadcrumbs(breadcrumbs.to_owned()),
484 format!("[{}] {}", term.id(), term.name()),
485 format!(
486 "Expected a boolean (true or false), but have {}",
487 value
488 ),
489 )));
490
491 CVParamValue::String(value.to_string())
492 }
493 },
494 Some(ValueType::Int) => CVParamValue::Integer(value.parse().unwrap()),
495 Some(ValueType::NonNegativeInteger) => match value.parse() {
496 Ok(value) => CVParamValue::NonNegativeInteger(value),
497 Err(error) => {
498 errors.push_back(ParseError::InvalidValue((
501 Breadcrumbs(breadcrumbs.to_owned()), format!("[{}] {}", term.id(), term.name()),
503 format!(
504 "Expected a non-negative integer, but have {}: {:?}",
505 value, error
506 ),
507 )));
508
509 CVParamValue::String(value.to_string())
510 }
511 },
512 Some(ValueType::Float) => match value.parse() {
513 Ok(value) => CVParamValue::Float(value),
514 Err(error) => {
515 errors.push_back(ParseError::InvalidValue((
516 Breadcrumbs(breadcrumbs.to_owned()), format!("[{}] {}", term.id(), term.name()),
518 format!("Expected a float, but have {}: {:?}", value, error),
519 )));
520
521 CVParamValue::String(value.to_string())
522 }
523 },
524 Some(ValueType::NonNegativeFloat) => {
525 let float = value.parse().unwrap();
526
527 if float < 0.0 {
528 errors.push_back(ParseError::InvalidValue((
529 Breadcrumbs(breadcrumbs.to_owned()), format!("[{}] {}", term.id(), term.name()),
531 format!("Expected a non-negative float, but have {}", value),
532 )));
533 }
534
535 CVParamValue::Float(float)
536 }
537 Some(ValueType::DateTime) => CVParamValue::DateTime(value.to_string()),
538 Some(value_type) => {
539 println!("Unknown type {:?} for term {}", value_type, term.id());
540 CVParamValue::Empty
541 }
542 None => {
543 errors.push_back(ParseError::UnexpectedValue((
544 Breadcrumbs(breadcrumbs.to_owned()), format!("[{}] {}", term.id(), term.name()),
546 value.to_string(),
547 )));
548
549 CVParamValue::String(value.to_string())
550 }
551 };
552
553 cv_param = CVParam::new(term, param_value);
554 }
555 }
556 None => {
557 cv_param = CVParam::new(term, CVParamValue::Empty);
558 }
559 }
560
561 if let Some(unit_accession) = &self.unit_accession {
562 let unit_accession = self.decoder.decode(unit_accession).unwrap();
563
564 if let Some(unit_term) = self.ontology.get(&unit_accession) {
566 cv_param.set_unit(unit_term.clone());
567 } else {
568 panic!("Unknown ontology term: {:?}", self.accession());
569 }
570 }
571
572 cv_param
573 }
574}
575
576#[derive(Debug)]
578pub struct CVParam {
579 term: Accession, value: CVParamValue,
581
582 unit_term: Option<OBOTerm>,
583}
584
585impl Clone for CVParam {
586 fn clone(&self) -> Self {
587 Self {
588 term: self.term.clone(),
589 value: self.value.clone(),
590 unit_term: self.unit_term.clone(),
591 }
592 }
593}
594
595impl CVParam {
596 pub fn new(term: Accession, value: CVParamValue) -> Self {
598 CVParam {
604 term,
608 value,
609 unit_term: None,
610 }
611 }
612
613 #[inline]
617 pub fn term(&self) -> Option<&Term> {
618 match &self.term {
619 Accession::Term(term) => Some(term),
620 Accession::String {
621 accession: _,
622 name: _,
623 } => None,
624 }
625 }
626
627 #[inline]
629 pub fn accession(&self) -> &str {
630 match &self.term {
631 Accession::Term(term) => term.id(),
632 Accession::String { accession, name: _ } => accession,
633 }
634 }
635
636 #[inline]
638 pub fn cv_ref(&self) -> &str {
639 let mut splitter = self.term.id().splitn(2, ':');
640 splitter.next().unwrap()
641 }
642
643 #[inline]
645 pub fn name(&self) -> &str {
646 match &self.term {
647 Accession::Term(term) => term.name().unwrap(),
648 Accession::String { accession: _, name } => name,
649 }
650 }
651
652 #[inline]
654 pub fn value(&self) -> &CVParamValue {
655 &self.value
656 }
657
658 #[inline]
660 pub fn value_as_u32(&self) -> Option<u32> {
661 self.value.as_u32()
662 }
663
664 #[inline]
666 pub fn value_as_u64(&self) -> Option<u64> {
667 self.value.as_u64()
668 }
669
670 #[inline]
672 pub fn value_as_f64(&self) -> Option<f64> {
673 self.value.as_f64()
674 }
675
676 #[inline]
679 pub fn set_unit(&mut self, unit_term: OBOTerm) {
680 self.unit_term = Some(unit_term);
681 }
682
683 #[inline]
685 pub fn has_unit(&self) -> bool {
686 self.unit_term.is_some()
687 }
688
689 #[inline]
691 pub fn unit_cv_ref(&self) -> Option<&str> {
692 if let Some(unit_term) = &self.unit_term {
693 let mut splitter = unit_term.id().splitn(2, ':');
694 Some(splitter.next().unwrap())
695 } else {
696 None
697 }
698 }
699
700 #[inline]
702 pub fn unit_accession(&self) -> Option<&str> {
703 if let Some(unit_term) = &self.unit_term {
704 Some(unit_term.id())
705 } else {
706 None
707 }
708 }
709
710 #[inline]
712 pub fn unit_name(&self) -> Option<&str> {
713 if let Some(unit_term) = &self.unit_term {
714 Some(unit_term.name().unwrap())
715 } else {
716 None
717 }
718 }
719}
720
721impl MzMLTag for CVParam {
722 fn parse_start_tag<B: BufRead>(
723 parser: &mut MzMLReader<B>,
724 start_event: &BytesStart,
725 ) -> Result<Option<Self>, FatalParseError>
726 where
727 Self: std::marker::Sized,
728 {
729 if start_event.name().local_name().as_ref() != b"cvParam" {
730 Err(FatalParseError::UnexpectedTag(format!(
731 "Unexpected event {:?} when processing CVParam",
732 start_event,
733 )))
734 } else {
735 let mut accession: Option<Cow<[u8]>> = None;
736 let mut name: Option<Cow<[u8]>> = None;
737 let mut value: Option<Cow<[u8]>> = None;
738
739 let mut unit_accession: Option<Cow<[u8]>> = None;
740 let mut unit_name: Option<Cow<[u8]>> = None;
741
742 for att in start_event
743 .attributes()
744 .with_checks(parser.with_attribute_checks)
745 {
746 match att {
747 Ok(att) => {
748 match att.key.as_ref() {
749 b"cvRef" => {}
751 b"accession" => {
752 accession = Some(att.value);
753 }
754 b"name" => {
755 name = Some(att.value);
756 }
757 b"value" => {
758 value = Some(att.value);
759 }
760 b"unitCvRef" => {}
762 b"unitAccession" => {
763 unit_accession = Some(att.value);
764 }
765 b"unitName" => {
766 unit_name = Some(att.value);
767 }
768 _ => {
769 parser.errors.push_back(ParseError::UnexpectedAttribute((
770 Tag::CVParam,
771 std::str::from_utf8(att.key.as_ref())?.to_string(),
772 )));
773 }
774 }
775 }
776 Err(error) => {
777 parser
778 .errors
779 .push_back(ParseError::XMLError((Tag::CVParam, error.into())));
780 }
781 }
782 }
783
784 match (accession, name) {
785 (Some(accession), Some(name)) => {
786 if accession.is_empty() {
787 parser.errors.push_back(ParseError::MissingAttribute((
788 Tag::CVParam,
789 "No accession found for CVParam".to_string(),
790 )));
791
792 Ok(None)
793 } else {
794 let raw_term = RawTerm {
795 ontology: parser.ontology.clone(),
796 decoder: parser.reader.decoder(),
797 accession,
798 name,
799 value,
800 unit_accession,
801 unit_name,
802 };
804
805 Ok(Some(
806 raw_term.to_cv_param(&parser.breadcrumbs, &mut parser.errors),
807 ))
808 }
809 }
810 _ => {
811 parser.errors.push_back(ParseError::MissingAttribute((
812 Tag::CVParam,
813 "No accession found for CVParam".to_string(),
814 )));
815
816 Ok(None)
817 }
818 }
819 }
820 }
821
822 fn tag() -> Tag {
823 Tag::CVParam
824 }
825
826 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
827 let mut elem = BytesStart::new("cvParam");
828 elem.push_attribute(("cvRef", self.cv_ref()));
829 elem.push_attribute(("accession", self.accession()));
830 elem.push_attribute(("name", self.name()));
831
832 match self.value() {
833 CVParamValue::Empty => {}
834 CVParamValue::Boolean(value) => {
835 if *value {
836 elem.push_attribute(("value", "true"))
837 } else {
838 elem.push_attribute(("value", "false"))
839 }
840 }
841 CVParamValue::String(value) => elem.push_attribute(("value", value.as_str())),
842 CVParamValue::Float(value) => {
843 elem.push_attribute(("value", value.to_string().as_str()))
844 }
845 CVParamValue::Integer(value) => {
846 elem.push_attribute(("value", value.to_string().as_str()))
847 }
848 CVParamValue::NonNegativeInteger(value) => {
849 elem.push_attribute(("value", value.to_string().as_str()))
850 }
851 CVParamValue::DateTime(value) => elem.push_attribute(("value", value.as_str())),
852 }
853
854 if self.has_unit() {
855 elem.push_attribute(("unitCvRef", self.unit_cv_ref().unwrap()));
856 elem.push_attribute(("unitAccession", self.unit_accession().unwrap()));
857 elem.push_attribute(("unitName", self.unit_name().unwrap()));
858 }
859
860 writer.write_event(Event::Empty(elem))
861 }
862
863 fn parse_xml<B: BufRead>(
864 &mut self,
865 _parser: &mut MzMLReader<B>,
866 _buffer: &mut Vec<u8>,
867 ) -> Result<(), FatalParseError> {
868 Ok(())
869 }
870}
871
872#[derive(Debug, Clone)]
874pub enum Accession {
875 Term(OBOTerm),
877 String {
879 accession: String,
881 name: String,
883 },
884}
885
886impl From<OBOTerm> for Accession {
887 fn from(term: OBOTerm) -> Self {
888 Accession::Term(term)
889 }
890}
891
892impl From<&OBOTerm> for Accession {
893 fn from(term: &OBOTerm) -> Self {
894 Accession::Term(term.clone())
895 }
896}
897
898impl Accession {
899 pub fn id(&self) -> &str {
901 match self {
902 Accession::Term(term) => term.id(),
903 Accession::String { accession, name: _ } => accession,
904 }
905 }
906
907 pub fn name(&self) -> &str {
909 match self {
910 Accession::Term(term) => term.name().unwrap(),
911 Accession::String { accession: _, name } => name,
912 }
913 }
914
915 pub fn value_type(&self) -> Option<ValueType> {
917 match self {
918 Accession::Term(term) => term.value_type(),
919 Accession::String {
920 accession: _,
921 name: _,
922 } => None,
923 }
924 }
925}
926
927#[derive(Debug)]
929pub struct UserParam {
930 name: String,
931 data_type: Option<String>,
932 value: Option<String>,
933 unit_term: Option<Accession>,
934}
935
936impl Clone for UserParam {
937 fn clone(&self) -> Self {
938 Self {
939 name: self.name.clone(),
940 data_type: self.data_type.clone(),
941 value: self.value.clone(),
942 unit_term: self.unit_term.clone(),
943 }
944 }
945}
946
947impl UserParam {
948 pub fn new(name: &str) -> Self {
950 Self {
951 name: name.to_string(),
952 data_type: None,
953 value: None,
954 unit_term: None,
955 }
956 }
957
958 pub fn name(&self) -> &str {
960 &self.name
961 }
962}
963
964impl MzMLTag for UserParam {
965 fn parse_start_tag<B: BufRead>(
966 parser: &mut MzMLReader<B>,
967 start_event: &BytesStart,
968 ) -> Result<Option<Self>, FatalParseError>
969 where
970 Self: std::marker::Sized,
971 {
972 if start_event.name().local_name().as_ref() != b"userParam" {
973 Err(FatalParseError::UnexpectedTag(format!(
974 "Unexpected event {:?} when processing UserParam",
975 start_event,
976 )))
977 } else {
978 let mut name: Option<Cow<[u8]>> = None;
979 let mut data_type: Option<Cow<[u8]>> = None;
980 let mut value: Option<Cow<[u8]>> = None;
981
982 let mut unit_accession: Option<Cow<[u8]>> = None;
983 let mut unit_name: Option<Cow<[u8]>> = None;
984
985 for att in start_event
986 .attributes()
987 .with_checks(parser.with_attribute_checks)
988 {
989 match att {
990 Ok(att) => {
991 match att.key.as_ref() {
992 b"name" => {
993 name = Some(att.value);
994 }
995 b"value" => {
996 value = Some(att.value);
997 }
998 b"type" => {
999 data_type = Some(att.value);
1000 }
1001 b"unitCvRef" => {}
1003 b"unitAccession" => {
1004 unit_accession = Some(att.value);
1005 }
1006 b"unitName" => {
1007 unit_name = Some(att.value);
1008 }
1009 _ => {
1010 parser.errors.push_back(ParseError::UnexpectedAttribute((
1011 Tag::UserParam,
1012 std::str::from_utf8(att.key.as_ref())?.to_string(),
1013 )));
1014 }
1015 }
1016 }
1017 Err(error) => {
1018 parser
1019 .errors
1020 .push_back(ParseError::XMLError((Tag::UserParam, error.into())));
1021 }
1022 };
1023 }
1024
1025 let mut user_param = match name {
1026 Some(name) => {
1027 UserParam::new(parser.parse_string(Tag::UserParam, &name).unwrap_or(""))
1028 }
1029 None => {
1030 return Err(FatalParseError::InvalidTag((
1031 Tag::UserParam,
1032 "No name attribute supplied for the <userParam> tag".to_string(),
1033 )));
1034 }
1035 };
1036
1037 if let Some(value) = value {
1038 user_param.value = parser
1039 .parse_string(Tag::UserParam, &value)
1040 .map(|value| value.to_string());
1041 }
1042
1043 if let Some(data_type) = data_type {
1044 user_param.data_type = parser
1045 .parse_string(Tag::UserParam, &data_type)
1046 .map(|data_type| data_type.to_string());
1047 }
1048
1049 if let (Some(unit_accession), Some(unit_name)) = (unit_accession, unit_name) {
1050 user_param.unit_term = Some(Accession::String {
1051 accession: parser
1052 .parse_string(Tag::UserParam, &unit_accession)
1053 .map(|accession| accession.to_string())
1054 .unwrap_or_else(|| "".to_string()),
1055 name: parser
1056 .parse_string(Tag::UserParam, &unit_name)
1057 .map(|unit_name| unit_name.to_string())
1058 .unwrap_or_else(|| "".to_string()),
1059 });
1060 }
1061
1062 Ok(Some(user_param))
1063 }
1064 }
1065
1066 fn parse_xml<B: BufRead>(
1067 &mut self,
1068 _parser: &mut MzMLReader<B>,
1069 _buffer: &mut Vec<u8>,
1070 ) -> Result<(), FatalParseError> {
1071 Ok(())
1072 }
1073
1074 fn tag() -> Tag {
1075 Tag::UserParam
1076 }
1077
1078 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
1079 let mut elem = BytesStart::new("userParam");
1080 elem.push_attribute(("name", self.name()));
1081
1082 if let Some(data_type) = &self.data_type {
1083 elem.push_attribute(("type", data_type.as_str()));
1084 }
1085
1086 if let Some(value) = &self.value {
1087 elem.push_attribute(("value", value.as_str()));
1088 }
1089
1090 if let Some(unit_term) = &self.unit_term {
1091 let mut splitter = unit_term.id().splitn(2, ':');
1092 let cv_ref = splitter.next().unwrap();
1093
1094 elem.push_attribute(("unitCvRef", cv_ref));
1095 elem.push_attribute(("unitAccession", unit_term.id()));
1096 elem.push_attribute(("unitName", unit_term.name()));
1097 }
1098
1099 writer.write_event(Event::Empty(elem))
1100 }
1101}