1use std::{
2 borrow::Cow,
3 io::{BufRead, Write},
4};
5
6use quick_xml::events::{BytesStart, Event};
7
8use crate::{
9 mzml::attributes::{AttributeValue, LIST_ATTRIBUTES},
10 FatalParseError, ParseError, Tag,
11};
12
13use super::{writer::Writer, MzMLReader, MzMLTag};
14
15pub struct CVList {
16 pub(crate) cvs: Vec<CV>,
18}
19
20impl CVList {
21 fn new(count: usize) -> Self {
22 CVList {
23 cvs: Vec::with_capacity(count),
24 }
25 }
26}
27
28impl MzMLTag for CVList {
29 fn parse_start_tag<B: BufRead>(
30 parser: &mut MzMLReader<B>,
31 start_event: &BytesStart,
32 ) -> Result<Option<Self>, FatalParseError>
33 where
34 Self: std::marker::Sized,
35 {
36 if start_event.name().local_name().as_ref() != b"cvList" {
37 Err(FatalParseError::UnexpectedTag(format!(
38 "Unexpected event {:?} when processing CVList",
39 start_event,
40 )))
41 } else {
42 let attributes =
43 parser.process_attributes(Tag::CVList, &LIST_ATTRIBUTES, start_event)?;
44
45 let count = match attributes.get("count") {
46 Some(&AttributeValue::Integer(count)) => count as usize,
47 _ => 0,
48 };
49
50 parser.breadcrumbs.push_back((Tag::CVList, None));
51
52 Ok(Some(CVList::new(count)))
53 }
54 }
55
56 fn parse_xml<B: BufRead>(
57 &mut self,
58 parser: &mut MzMLReader<B>,
59 buffer: &mut Vec<u8>,
60 ) -> Result<(), FatalParseError> {
61 loop {
63 buffer.clear();
65
66 let next_event = parser.next(buffer)?;
67
68 match next_event {
69 Event::Start(start_event) | Event::Empty(start_event) => {
70 match start_event.name().as_ref() {
71 b"cv" => {
72 if let Some(cv) = CV::parse_start_tag(parser, &start_event)? {
73 self.cvs.push(cv);
74 }
75 }
76 _ => parser.errors.push_back(ParseError::UnexpectedTag(format!(
77 "{:?} unexpected when processing {:?}",
78 std::str::from_utf8(start_event.name().as_ref()),
79 Tag::CVList
80 ))),
81 }
82 }
83 Event::End(end_event) => {
84 if let b"cvList" = end_event.name().as_ref() {
85 parser.breadcrumbs.pop_back();
86
87 break;
88 }
89 }
90 Event::Eof => {
91 return Err(FatalParseError::MissingClosingTag("cvList".to_string()));
92 }
93 _ => {}
94 }
95 }
96
97 Ok(())
98 }
99
100 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
101 writer.write_list("cvList", &self.cvs)
102 }
114
115 #[inline]
116 fn tag() -> Tag {
117 Tag::CVList
118 }
119}
120
121pub struct CV {
122 uri: String,
123 full_name: String,
124 id: String,
125 version: Option<String>,
126}
127
128impl Clone for CV {
129 fn clone(&self) -> Self {
130 Self {
131 uri: self.uri.clone(),
132 full_name: self.full_name.clone(),
133 id: self.id.clone(),
134 version: self.version.clone(),
135 }
136 }
137}
138
139impl CV {
140 pub fn new(id: &str, uri: &str, full_name: &str) -> CV {
141 CV {
142 id: id.into(),
143 uri: uri.into(),
144 full_name: full_name.into(),
145 version: None,
146 }
147 }
148
149 #[inline]
150 pub fn set_version(&mut self, version: &str) {
151 self.version = Some(version.into());
152 }
153}
154
155impl MzMLTag for CV {
156 fn parse_start_tag<B: BufRead>(
157 parser: &mut MzMLReader<B>,
158 start_event: &BytesStart,
159 ) -> Result<Option<Self>, FatalParseError>
160 where
161 Self: std::marker::Sized,
162 {
163 if start_event.name().local_name().as_ref() != b"cv" {
164 Err(FatalParseError::UnexpectedTag(format!(
165 "Unexpected event {:?} when processing CV",
166 start_event,
167 )))
168 } else {
169 let mut id: Option<Cow<[u8]>> = None;
170 let mut uri: Option<Cow<[u8]>> = None;
171 let mut full_name: Option<Cow<[u8]>> = None;
172 let mut version: Option<Cow<[u8]>> = None;
173
174 for attribute in start_event
175 .attributes()
176 .with_checks(parser.with_attribute_checks)
177 {
178 match attribute {
179 Ok(attribute) => match attribute.key.as_ref() {
180 b"id" => {
181 id = Some(attribute.value);
182 }
183 b"URI" | b"uri" => {
184 uri = Some(attribute.value);
185 }
186 b"fullName" => {
187 full_name = Some(attribute.value);
188 }
189 b"version" => {
190 version = Some(attribute.value);
191 }
192 _ => {
193 parser.errors.push_back(ParseError::UnexpectedAttribute((
194 Tag::CV,
195 std::str::from_utf8(attribute.key.as_ref())?.to_string(),
196 )));
197 }
198 },
199 Err(error) => {
200 parser
201 .errors
202 .push_back(ParseError::XMLError((Tag::CV, error.into())));
203 }
204 };
205 }
206
207 let id = match &id {
208 Some(id) => parser.parse_string(Tag::CV, id).unwrap_or(""),
209 None => {
210 parser
211 .errors
212 .push_back(ParseError::MissingAttribute((Tag::CV, "id".to_string())));
213
214 ""
215 }
216 };
217
218 let uri = match &uri {
219 Some(uri) => parser.parse_string(Tag::CV, uri).unwrap_or(""),
220 None => {
221 parser
222 .errors
223 .push_back(ParseError::MissingAttribute((Tag::CV, "uri".to_string())));
224
225 ""
226 }
227 };
228
229 let full_name = match &full_name {
230 Some(full_name) => parser.parse_string(Tag::CV, full_name).unwrap_or(""),
231 None => {
232 parser.errors.push_back(ParseError::MissingAttribute((
233 Tag::CV,
234 "full_name".to_string(),
235 )));
236
237 ""
238 }
239 };
240
241 let mut cv = CV::new(id, uri, full_name);
242
243 if let Some(version) = version {
244 cv.set_version(parser.parse_string(Tag::CV, &version).unwrap_or(""));
245 }
246
247 Ok(Some(cv))
248 }
249 }
250
251 fn parse_xml<B: BufRead>(
252 &mut self,
253 _parser: &mut MzMLReader<B>,
254 _buffer: &mut Vec<u8>,
255 ) -> Result<(), FatalParseError> {
256 Ok(())
257 }
258
259 fn tag() -> Tag {
260 Tag::CV
261 }
262
263 fn write_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), quick_xml::Error> {
264 let mut elem = BytesStart::new("cv");
265 elem.push_attribute(("uri", self.uri.as_ref()));
266 elem.push_attribute(("fullName", self.full_name.as_ref()));
267 elem.push_attribute(("id", self.id.as_ref()));
268
269 if let Some(ref version) = self.version {
270 elem.push_attribute(("version", version.as_ref()));
271 }
272
273 writer.write_event(Event::Empty(elem))
274 }
275}