imzml/mzml/
cvlist.rs

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    // TODO: Currently need this to avoid breaking code with the old cv_list: Vec<CV> parameter on MzML struct
17    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        // Check what comes next
62        loop {
63            // Clear the buffer ready for the next tag
64            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        // let mut elem = BytesStart::owned(b"cvList".to_vec(), "cvList".len());
103        // // encodedLength is required
104        // elem.push_attribute(("count", format!("{}", self.cvs.len()).as_str()));
105
106        // writer.write_event(Event::Start(elem))?;
107
108        // for item in &self.cvs {
109        //     item.write_xml(writer)?;
110        // }
111
112        // writer.write_event(Event::End(BytesEnd::owned(b"cvList".to_vec())))
113    }
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}