1use autosar_data_specification::{
2 AttributeName, AttributeSpec, AutosarVersion, CharacterDataSpec, ContentMode, ElementMultiplicity, ElementName,
3 ElementType, EnumItem,
4};
5use smallvec::SmallVec;
6use std::borrow::Cow;
7use std::collections::HashSet;
8use std::path::PathBuf;
9use std::str::FromStr;
10use std::str::Utf8Error;
11use thiserror::Error;
12
13use crate::lexer::{ArxmlEvent, ArxmlLexer};
14use crate::{
15 Attribute, AutosarDataError, CharacterData, Element, ElementContent, ElementOrModel, ElementRaw, ReferenceBaseInfo,
16 WeakElement,
17};
18
19#[derive(Debug, Error, PartialEq)]
20#[non_exhaustive]
21pub enum ArxmlParserError {
23 #[error("Invalid arxml file: bad file header")]
25 InvalidArxmlFileHeader,
26
27 #[error("Unexpeded XML file header found inside ARXML data")]
29 UnexpectedXmlFileHeader {
30 element: ElementName,
32 },
33
34 #[error("Unknown Autosar xsd file {input_verstring} referenced in the file header")]
36 UnknownAutosarVersion {
37 input_verstring: String,
39 },
40
41 #[error("Invalid Autosar xsd file {input_verstring} referenced in the file header should be replaced with {}", .replacement.filename())]
43 InvalidAutosarVersion {
44 input_verstring: String,
46 replacement: AutosarVersion,
48 },
49
50 #[error("Encountered unexpected child element {sub_element} inside element {element}")]
52 IncorrectBeginElement {
53 element: ElementName,
55 sub_element: ElementName,
57 },
58
59 #[error(
61 "Encountered invalid child element {invalid_element} inside parent element {element}. {invalid_element} is not a known Autosar element."
62 )]
63 InvalidBeginElement {
64 element: ElementName,
66 invalid_element: String,
68 },
69
70 #[error("Encountered the closing tag for element {other_element}, but element {element} was open.")]
72 IncorrectEndElement {
73 element: ElementName,
75 other_element: ElementName,
77 },
78
79 #[error(
81 "Encountered invalid end tag for element {invalid_element} inside parent element {parent_element}. {invalid_element} is not a known Autosar element."
82 )]
83 InvalidEndElement {
84 parent_element: ElementName,
86 invalid_element: String,
88 },
89
90 #[error("Multiple conflicting sub elements have been added to element {element}. The latest was {sub_element}.")]
92 ElementChoiceConflict {
93 element: ElementName,
95 sub_element: ElementName,
97 },
98
99 #[error("Element {sub_element} exists in {element}, but is not allowed in {version}")]
101 ElementVersionError {
102 element: ElementName,
104 sub_element: ElementName,
106 version: AutosarVersion,
108 },
109
110 #[error("Only one {sub_element} is allowed inside {element}, but another occurrence was found")]
112 TooManySubElements {
113 element: ElementName,
115 sub_element: ElementName,
117 },
118
119 #[error("The required sub element {sub_element} was not found in element {element}")]
121 RequiredSubelementMissing {
122 element: ElementName,
124 sub_element: ElementName,
126 },
127
128 #[error("Could not parse the attribute text \"{attribute_text}\" in element {element}")]
130 AttributeValueError {
131 element: ElementName,
133 attribute_text: String,
135 },
136
137 #[error("Element {element} contains unknown attribute {attribute}")]
139 UnknownAttributeError {
140 element: ElementName,
142 attribute: String,
144 },
145
146 #[error("Attribute {attribute} exists in element {element}, but is not allowed in {version}")]
148 AttributeVersionError {
149 element: ElementName,
151 attribute: AttributeName,
153 version: AutosarVersion,
155 },
156
157 #[error("Attribute {attribute} is required in element {element}, but was not found")]
159 RequiredAttributeMissing {
160 element: ElementName,
162 attribute: AttributeName,
164 },
165
166 #[error("Character content found, which is not allowed inside element {element}")]
168 CharacterContentForbidden {
169 element: ElementName,
171 },
172
173 #[error("enum item {enum_item} is a valid value in element {element}, but is not allowed in {version}")]
175 EnumItemVersionError {
176 element: ElementName,
178 enum_item: EnumItem,
180 version: AutosarVersion,
182 },
183
184 #[error("string {value} is not a valid enum item")]
186 UnknownEnumItem {
187 value: String,
189 },
190
191 #[error("enum item {item} is not valid in element {element}")]
193 InvalidEnumItem {
194 element: ElementName,
196 item: EnumItem,
198 },
199
200 #[error("string value {value} is too long: max length is {length}")]
202 StringValueTooLong {
203 value: String,
205 length: usize,
207 },
208
209 #[error("string value {value} is not matched by the validation regex {regex}")]
211 RegexMatchError {
212 value: String,
214 regex: String,
216 },
217
218 #[error("could not convert value to utf-8: {source}")]
220 Utf8Error {
221 source: Utf8Error,
223 },
224
225 #[error("Unexpected end of file while parsing element {element}")]
227 UnexpectedEndOfFile {
228 element: ElementName,
230 },
231
232 #[error("Failed to parse {input} as a number")]
234 InvalidNumber {
235 input: String,
237 },
238
239 #[error("Additional data found in the input after the final </AUTOSAR> element")]
241 AdditionalDataError,
242
243 #[error("Invalid XML entity in {input}")]
245 InvalidXmlEntity {
246 input: String,
248 },
249}
250
251pub(crate) struct ArxmlParser<'a> {
252 filename: PathBuf,
253 line: usize,
254 buffer: &'a [u8],
255 fileversion: AutosarVersion,
256 current_element: ElementName,
257 strict: bool,
258 version_compatibility: u32,
259 pub(crate) identifiables: Vec<(String, WeakElement)>,
260 pub(crate) references: Vec<(String, WeakElement, Option<String>)>,
261 pub(crate) reference_bases: Vec<(String, ReferenceBaseInfo)>,
262 pub(crate) warnings: Vec<AutosarDataError>,
263 standalone: Option<bool>,
264}
265
266impl<'a> ArxmlParser<'a> {
267 pub(crate) fn new(filename: PathBuf, buffer: &'a [u8], strict: bool) -> Self {
268 Self {
269 filename,
270 line: 1,
271 buffer,
272 fileversion: AutosarVersion::Autosar_4_0_1, current_element: ElementName::Autosar,
274 strict,
275 version_compatibility: u32::MAX,
276 identifiables: Vec::new(),
277 references: Vec::new(),
278 reference_bases: Vec::new(),
279 warnings: Vec::new(),
280 standalone: None,
281 }
282 }
283
284 fn next<'b>(&mut self, lexer: &'b mut ArxmlLexer) -> Result<ArxmlEvent<'b>, AutosarDataError> {
285 let (line, event) = lexer.next()?;
286 self.line = line;
287 Ok(event)
288 }
289
290 pub(crate) fn error(&self, err: ArxmlParserError) -> AutosarDataError {
291 AutosarDataError::ParserError {
292 filename: self.filename.clone(),
293 line: self.line,
294 source: err,
295 }
296 }
297
298 pub(crate) fn optional_error(&mut self, err: ArxmlParserError) -> Result<(), AutosarDataError> {
299 let wrapped_err = AutosarDataError::ParserError {
300 filename: self.filename.clone(),
301 line: self.line,
302 source: err,
303 };
304 if self.strict {
305 Err(wrapped_err)
306 } else {
307 self.warnings.push(wrapped_err);
308 Ok(())
309 }
310 }
311
312 fn check_version(&mut self, item_version: u32, error: ArxmlParserError) -> Result<(), AutosarDataError> {
313 self.version_compatibility &= item_version;
314 if (self.fileversion as u32) & item_version == 0 {
315 self.optional_error(error)
316 } else {
317 Ok(())
318 }
319 }
320
321 pub(crate) fn parse_arxml(&mut self) -> Result<Element, AutosarDataError> {
323 let mut lexer = ArxmlLexer::new(self.buffer, self.filename.clone());
324
325 if let ArxmlEvent::ArxmlHeader(standalone) = self.next(&mut lexer)? {
326 self.standalone = standalone;
327 } else {
328 return Err(self.error(ArxmlParserError::InvalidArxmlFileHeader));
329 }
330
331 let mut stored_comment = None;
332 let mut token = self.next(&mut lexer)?;
333 while let ArxmlEvent::Comment(comment_bytes) = token {
334 stored_comment = Some(String::from_utf8_lossy(comment_bytes).into());
335 token = self.next(&mut lexer)?;
336 }
337
338 if let ArxmlEvent::BeginElement(elemname, attributes_text) = token
339 && let Ok(ElementName::Autosar) = ElementName::from_bytes(elemname)
340 {
341 let attributes = self.parse_attribute_text(ElementType::ROOT, attributes_text)?;
342 self.parse_file_header(&attributes)?;
343
344 let new_element = ElementRaw {
345 parent: ElementOrModel::None,
346 elemname: ElementName::Autosar,
347 elemtype: ElementType::ROOT,
348 content: SmallVec::new(),
349 attributes,
350 file_membership: HashSet::with_capacity(0),
351 comment: stored_comment,
352 };
353 let path = Cow::from("");
354 let autosar_root_element = self.parse_element(new_element, path, &mut lexer)?;
355 self.verify_end_of_input(&mut lexer)?;
356
357 return Ok(autosar_root_element);
358 }
359 Err(self.error(ArxmlParserError::InvalidArxmlFileHeader))
360 }
361
362 fn parse_file_header(&mut self, attributes: &SmallVec<[Attribute; 1]>) -> Result<(), AutosarDataError> {
364 let attr_xmlns = attributes.iter().find(|attr| attr.attrname == AttributeName::xmlns);
365 let attr_xsi = attributes.iter().find(|attr| attr.attrname == AttributeName::xmlnsXsi);
366 let attr_schema = attributes
367 .iter()
368 .find(|attr| attr.attrname == AttributeName::xsiSchemalocation);
369 if let (
370 Some(Attribute {
371 content: CharacterData::String(xmlns),
372 ..
373 }),
374 Some(Attribute {
375 content: CharacterData::String(xsi),
376 ..
377 }),
378 Some(Attribute {
379 content: CharacterData::String(schema),
380 ..
381 }),
382 ) = (attr_xmlns, attr_xsi, attr_schema)
383 {
384 if xmlns != "http://autosar.org/schema/r4.0" || xsi != "http://www.w3.org/2001/XMLSchema-instance" {
385 return Err(self.error(ArxmlParserError::InvalidArxmlFileHeader));
386 }
387 self.fileversion = self.parse_file_version(schema)?;
388
389 Ok(())
390 } else {
391 Err(self.error(ArxmlParserError::InvalidArxmlFileHeader))
392 }
393 }
394
395 fn parse_file_version(&mut self, schema: &str) -> Result<AutosarVersion, AutosarDataError> {
397 let mut schema_parts = schema.split(' ');
398 let schema_base = schema_parts.next().unwrap_or("");
399 if schema_base != "http://autosar.org/schema/r4.0" {
400 return Err(self.error(ArxmlParserError::InvalidArxmlFileHeader));
401 }
402 let xsd_file_raw = schema_parts.next().unwrap_or("");
403 let xsd_file: String = if xsd_file_raw.starts_with("autosar") {
404 format!("AUTOSAR{}", xsd_file_raw.strip_prefix("autosar").unwrap())
405 } else {
406 xsd_file_raw.to_owned()
407 };
408 let version = if let Ok(autosar_version) = AutosarVersion::from_str(&xsd_file) {
409 autosar_version
410 } else if xsd_file == "AUTOSAR_4-3-1.xsd" {
411 self.optional_error(ArxmlParserError::InvalidAutosarVersion {
414 input_verstring: xsd_file.to_string(),
415 replacement: AutosarVersion::Autosar_00044,
416 })?;
417 AutosarVersion::Autosar_00044
418 } else if xsd_file == "AUTOSAR_4-4-0.xsd" {
419 self.optional_error(ArxmlParserError::InvalidAutosarVersion {
422 input_verstring: xsd_file.to_string(),
423 replacement: AutosarVersion::Autosar_00046,
424 })?;
425 AutosarVersion::Autosar_00046
426 } else if xsd_file == "AUTOSAR_4-5-0.xsd" {
427 self.optional_error(ArxmlParserError::InvalidAutosarVersion {
430 input_verstring: xsd_file.to_string(),
431 replacement: AutosarVersion::Autosar_00048,
432 })?;
433 AutosarVersion::Autosar_00048
434 } else {
435 self.optional_error(ArxmlParserError::UnknownAutosarVersion {
436 input_verstring: xsd_file.to_string(),
437 })?;
438 AutosarVersion::LATEST
439 };
440 Ok(version)
441 }
442
443 pub(crate) fn get_standalone(&self) -> Option<bool> {
445 self.standalone
446 }
447
448 fn parse_element(
450 &mut self,
451 raw_element: ElementRaw,
452 mut path: Cow<str>,
453 lexer: &mut ArxmlLexer,
454 ) -> Result<Element, AutosarDataError> {
455 let wrapped_element = raw_element.wrap();
456 let mut element = wrapped_element.0.write();
457
458 let mut elem_idx: Vec<usize> = Vec::new();
459 let mut short_name_found = false;
460
461 let mut stored_comment = None;
462 loop {
463 self.current_element = element.elemname;
465 let arxmlevent = self.next(lexer)?;
466 match arxmlevent {
467 ArxmlEvent::BeginElement(elem_text, attr_text) => {
468 if let Ok(name) = ElementName::from_bytes(elem_text) {
469 let (sub_elemtype, idx) = self.find_element_in_spec_checked(name, element.elemtype)?;
470 self.check_element_conflict(name, element.elemtype, &elem_idx, &idx)?;
471 elem_idx = idx;
472
473 if !element.content.is_empty() {
475 self.check_multiplicity(name, element.elemtype, &elem_idx, &element)?;
476 }
477
478 let new_element = ElementRaw {
480 parent: ElementOrModel::Element(wrapped_element.downgrade()),
481 elemname: name,
482 elemtype: sub_elemtype,
483 content: SmallVec::new(),
484 attributes: self.parse_attribute_text(sub_elemtype, attr_text)?,
485 file_membership: HashSet::with_capacity(0),
486 comment: stored_comment,
487 };
488 let sub_element = self.parse_element(new_element, Cow::from(path.as_ref()), lexer)?;
489 stored_comment = None;
490 if name == ElementName::ShortName {
492 short_name_found = true;
493 let sub_element_inner = sub_element.0.read();
494 if let Some(ElementContent::CharacterData(CharacterData::String(name_string))) =
495 sub_element_inner.content.first()
496 {
497 let mut new_path = String::with_capacity(path.len() + name_string.len() + 1);
498 new_path.push_str(&path);
499 new_path.push('/');
500 new_path.push_str(name_string);
501 path = Cow::from(new_path.clone());
502 self.identifiables.push((new_path, wrapped_element.downgrade()));
503 }
504 }
505 element.content.push(ElementContent::Element(sub_element));
506 } else {
507 return Err(self.error(ArxmlParserError::InvalidBeginElement {
508 element: element.elemname,
509 invalid_element: String::from_utf8_lossy(elem_text).to_string(),
510 }));
511 }
512 }
513 ArxmlEvent::EndElement(elem_text) => {
514 if let Ok(name) = ElementName::from_bytes(elem_text) {
515 if name == element.elemname {
516 break;
517 }
518 return Err(self.error(ArxmlParserError::IncorrectEndElement {
519 element: element.elemname,
520 other_element: name,
521 }));
522 }
523 return Err(self.error(ArxmlParserError::InvalidEndElement {
524 parent_element: element.elemname,
525 invalid_element: String::from_utf8_lossy(elem_text).to_string(),
526 }));
527 }
528 ArxmlEvent::Characters(text_content) => {
529 if let Some(character_data_spec) = element.elemtype.chardata_spec() {
530 let value = self.parse_character_data(text_content, character_data_spec)?;
531 if element.elemtype.is_ref()
532 && let CharacterData::String(refpath) = &value
533 {
534 let base = element
535 .attribute_value(AttributeName::Base)
536 .and_then(|cdata| cdata.string_value());
537 self.references
538 .push((refpath.to_owned(), wrapped_element.downgrade(), base));
539 }
540 element.content.push(ElementContent::CharacterData(value));
541 } else {
542 self.optional_error(ArxmlParserError::CharacterContentForbidden {
543 element: element.elemname,
544 })?;
545 }
546 }
547 ArxmlEvent::ArxmlHeader(_) => self.optional_error(ArxmlParserError::UnexpectedXmlFileHeader {
548 element: element.elemname,
549 })?,
550 ArxmlEvent::EndOfFile => {
551 return Err(self.error(ArxmlParserError::UnexpectedEndOfFile {
552 element: element.elemname,
553 }));
554 }
555 ArxmlEvent::Comment(comment_bytes) => {
556 stored_comment = Some(String::from_utf8_lossy(comment_bytes).into());
557 }
558 }
559 }
560
561 if short_name_found {
562 } else if element.elemtype.is_named_in_version(self.fileversion) {
563 self.optional_error(ArxmlParserError::RequiredSubelementMissing {
564 element: element.elemname,
565 sub_element: ElementName::ShortName,
566 })?;
567 }
568
569 if element.elemname == ElementName::ReferenceBase {
570 let mut short_label = None;
571 let mut package_ref = None;
572 let mut package_ref_base = None;
573
574 for item in &element.content {
575 if let ElementContent::Element(sub_elem) = item {
576 let sub_locked = sub_elem.0.read();
577 if sub_locked.elemname == ElementName::ShortLabel {
578 short_label = sub_locked.character_data().and_then(|cdata| cdata.string_value());
579 } else if sub_locked.elemname == ElementName::PackageRef {
580 package_ref = sub_locked.character_data().and_then(|cdata| cdata.string_value());
581 package_ref_base = sub_locked
582 .attribute_value(AttributeName::Base)
583 .and_then(|cdata| cdata.string_value());
584 }
585 }
586 }
587
588 if let (Some(short_label), Some(package_ref)) = (short_label, package_ref)
589 && !path.is_empty()
590 {
591 self.reference_bases.push((
592 short_label,
593 ReferenceBaseInfo {
594 owner_package_path: path.as_ref().to_string(),
595 package_ref,
596 package_ref_base,
597 },
598 ));
599 }
600 }
601
602 Ok(wrapped_element.clone())
603 }
604
605 fn find_element_in_spec_checked(
606 &mut self,
607 name: ElementName,
608 elemtype: ElementType,
609 ) -> Result<(ElementType, Vec<usize>), AutosarDataError> {
610 let (sub_elem_type, new_elem_indices) =
614 if let Some(result) = elemtype.find_sub_element(name, self.fileversion as u32) {
615 result
617 } else {
618 let (sub_elemtype, elem_idx) = elemtype.find_sub_element(name, u32::MAX).ok_or_else(|| {
620 self.error(ArxmlParserError::IncorrectBeginElement {
621 element: self.current_element,
622 sub_element: name,
623 })
624 })?;
625 let version_mask = elemtype.get_sub_element_version_mask(&elem_idx).unwrap();
628 self.check_version(
630 version_mask,
631 ArxmlParserError::ElementVersionError {
632 element: self.current_element,
633 sub_element: name,
634 version: self.fileversion,
635 },
636 )?;
637 (sub_elemtype, elem_idx)
638 };
639
640 Ok((sub_elem_type, new_elem_indices))
641 }
642
643 fn check_element_conflict(
644 &mut self,
645 name: ElementName,
646 elemtype: ElementType,
647 elem_indices: &[usize],
648 new_elem_indices: &Vec<usize>,
649 ) -> Result<(), AutosarDataError> {
650 if elem_indices.is_empty() || (elem_indices == new_elem_indices) {
651 } else {
654 let group_type = elemtype.find_common_group(elem_indices, new_elem_indices);
655 let mode = group_type.content_mode();
656
657 match mode {
658 ContentMode::Sequence => {
659 }
664 ContentMode::Choice => {
665 self.optional_error(ArxmlParserError::ElementChoiceConflict {
666 element: self.current_element,
667 sub_element: name,
668 })?;
669 }
670 ContentMode::Characters => {
671 panic!("accepted a sub-element inside a character-only element");
673 }
674 _ => {}
675 }
676 }
677 Ok(())
678 }
679
680 fn check_multiplicity(
681 &mut self,
682 name: ElementName,
683 elemtype: ElementType,
684 elem_idx: &[usize],
685 element: &ElementRaw,
686 ) -> Result<(), AutosarDataError> {
687 let datatype_mode = elemtype.get_sub_element_container_mode(elem_idx);
689 if (datatype_mode == ContentMode::Sequence || datatype_mode == ContentMode::Choice)
691 && let Some(multiplicity) = elemtype.get_sub_element_multiplicity(elem_idx)
692 {
693 if multiplicity != ElementMultiplicity::Any {
695 if element.content.iter().any(|ec| {
697 ec.unwrap_element()
698 .is_some_and(|subelem| subelem.element_name() == name)
699 }) {
700 self.optional_error(ArxmlParserError::TooManySubElements {
701 element: self.current_element,
702 sub_element: name,
703 })?;
704 }
705 }
706 }
707 Ok(())
708 }
709
710 fn parse_attribute_text(
711 &mut self,
712 elemtype: ElementType,
713 attributes_text: &[u8],
714 ) -> Result<SmallVec<[Attribute; 1]>, AutosarDataError> {
715 let mut attributes = SmallVec::new();
716 let startpos = attributes_text
719 .iter()
720 .position(|c| !c.is_ascii_whitespace())
721 .unwrap_or(0);
722 let mut rem = &attributes_text[startpos..];
723 while let Some(equals_pos) = rem.iter().position(|c| *c == b'=') {
724 let attr_name_part = &rem[..equals_pos];
725 if rem.len() - equals_pos < 3 {
726 break;
728 }
729 let quote_char = rem[equals_pos + 1];
730 if quote_char != b'"' && quote_char != b'\'' {
731 break;
733 }
734 rem = &rem[equals_pos + 2..];
735 let Some(endquote_pos) = rem.iter().position(|c| c == "e_char) else {
736 break;
738 };
739 let attr_value_part = &rem[..endquote_pos];
740
741 if let Ok(attr_name) = AttributeName::from_bytes(attr_name_part) {
742 if let Some(AttributeSpec {
743 spec: ctype,
744 version: version_mask,
745 ..
746 }) = elemtype.find_attribute_spec(attr_name)
747 {
748 self.check_version(
749 version_mask,
750 ArxmlParserError::AttributeVersionError {
751 element: self.current_element,
752 attribute: attr_name,
753 version: self.fileversion,
754 },
755 )?;
756 let attr_value = self.parse_character_data(attr_value_part, ctype)?;
757 attributes.push(Attribute {
758 attrname: attr_name,
759 content: attr_value,
760 });
761 } else {
762 self.optional_error(ArxmlParserError::UnknownAttributeError {
763 element: self.current_element,
764 attribute: attr_name.to_string(),
765 })?;
766 }
767 } else {
768 self.optional_error(ArxmlParserError::UnknownAttributeError {
769 element: self.current_element,
770 attribute: String::from_utf8_lossy(attr_name_part).to_string(),
771 })?;
772 }
773
774 let mut nextattr_start = endquote_pos + 1;
776 while nextattr_start < rem.len() && rem[nextattr_start].is_ascii_whitespace() {
777 nextattr_start += 1;
778 }
779
780 if nextattr_start < rem.len() && nextattr_start == endquote_pos + 1 {
782 break;
784 }
785 rem = &rem[nextattr_start..];
786 }
787
788 if !rem.is_empty() && !rem.iter().all(|c| c.is_ascii_whitespace()) {
789 self.optional_error(ArxmlParserError::AttributeValueError {
790 element: self.current_element,
791 attribute_text: String::from_utf8_lossy(attributes_text).into_owned(),
792 })?;
793 }
794
795 for (name, _ctype, required) in elemtype.attribute_spec_iter() {
796 if required && !attributes.iter().any(|attr: &Attribute| attr.attrname == name) {
797 self.optional_error(ArxmlParserError::RequiredAttributeMissing {
798 element: self.current_element,
799 attribute: name,
800 })?;
801 }
802 }
803
804 Ok(attributes)
805 }
806
807 fn parse_character_data(
808 &mut self,
809 input: &[u8],
810 character_data_spec: &CharacterDataSpec,
811 ) -> Result<CharacterData, AutosarDataError> {
812 let trimmed_input = trim_byte_string(input);
813 match character_data_spec {
814 CharacterDataSpec::Enum { items } => {
815 let value = EnumItem::from_bytes(trimmed_input).map_err(|_| {
816 self.error(ArxmlParserError::UnknownEnumItem {
817 value: String::from_utf8_lossy(trimmed_input).to_string(),
818 })
819 })?;
820 let (_, version) = items.iter().find(|(item, _)| *item == value).ok_or_else(|| {
821 self.error(ArxmlParserError::InvalidEnumItem {
822 element: self.current_element,
823 item: value,
824 })
825 })?;
826 self.check_version(
827 *version,
828 ArxmlParserError::EnumItemVersionError {
829 element: self.current_element,
830 enum_item: value,
831 version: self.fileversion,
832 },
833 )?;
834 Ok(CharacterData::Enum(value))
835 }
836 CharacterDataSpec::Pattern {
837 check_fn,
838 regex,
839 max_length,
840 } => {
841 if max_length.is_some() && trimmed_input.len() > max_length.unwrap() {
842 self.optional_error(ArxmlParserError::StringValueTooLong {
843 value: String::from_utf8_lossy(trimmed_input).to_string(),
844 length: max_length.unwrap(),
845 })?;
846 }
847 if !check_fn(trimmed_input) {
848 self.optional_error(ArxmlParserError::RegexMatchError {
849 value: String::from_utf8_lossy(trimmed_input).to_string(),
850 regex: (*regex).to_string(),
851 })?;
852 }
853 match std::str::from_utf8(trimmed_input) {
855 Ok(utf8string) => Ok(CharacterData::String(utf8string.to_owned())),
856 Err(err) => {
857 self.optional_error(ArxmlParserError::Utf8Error { source: err })?;
858 Ok(CharacterData::String(
859 String::from_utf8_lossy(trimmed_input).into_owned(),
860 ))
861 }
862 }
863 }
864 CharacterDataSpec::String {
865 preserve_whitespace,
866 max_length,
867 } => {
868 let raw_text = if *preserve_whitespace { input } else { trimmed_input };
869 if max_length.is_some() && raw_text.len() > max_length.unwrap() {
870 self.optional_error(ArxmlParserError::StringValueTooLong {
871 value: String::from_utf8_lossy(trimmed_input).to_string(),
872 length: max_length.unwrap(),
873 })?;
874 }
875 let text = match std::str::from_utf8(raw_text) {
876 Ok(utf8string) => Cow::from(utf8string),
877 Err(err) => {
878 self.optional_error(ArxmlParserError::Utf8Error { source: err })?;
879 String::from_utf8_lossy(raw_text)
880 }
881 };
882 let unescaped_text = self.unescape_string(&text)?.into_owned();
883 Ok(CharacterData::String(unescaped_text))
884 }
885 CharacterDataSpec::UnsignedInteger => {
886 let strval = std::str::from_utf8(trimmed_input)
887 .map_err(|err| self.error(ArxmlParserError::Utf8Error { source: err }))?;
888 let value = match strval.parse::<u64>() {
889 Ok(parsed) => parsed,
890 Err(_) => {
891 self.optional_error(ArxmlParserError::InvalidNumber {
892 input: strval.to_owned(),
893 })?;
894 0
895 }
896 };
897 Ok(CharacterData::UnsignedInteger(value))
898 }
899 CharacterDataSpec::Float => {
900 let strval = std::str::from_utf8(trimmed_input)
901 .map_err(|err| self.error(ArxmlParserError::Utf8Error { source: err }))?;
902 let value = match strval.parse::<f64>() {
903 Ok(parsed) => parsed,
904 Err(_) => {
905 self.optional_error(ArxmlParserError::InvalidNumber {
906 input: strval.to_owned(),
907 })?;
908 0.0
909 }
910 };
911 Ok(CharacterData::Float(value))
912 }
913 }
914 }
915
916 fn unescape_string<'b>(&mut self, input: &'b str) -> Result<Cow<'b, str>, AutosarDataError> {
917 if input.contains('&') {
918 let mut unescaped = String::with_capacity(input.len());
919 let mut rem = input;
920 while let Some(pos) = rem.find('&') {
921 unescaped.push_str(&rem[..pos]);
922 rem = &rem[pos..];
923 if rem.starts_with("<") {
924 unescaped.push('<');
925 rem = &rem[4..];
926 } else if rem.starts_with(">") {
927 unescaped.push('>');
928 rem = &rem[4..];
929 } else if rem.starts_with("&") {
930 unescaped.push('&');
931 rem = &rem[5..];
932 } else if rem.starts_with("'") {
933 unescaped.push('\'');
934 rem = &rem[6..];
935 } else if rem.starts_with(""") {
936 unescaped.push('"');
937 rem = &rem[6..];
938 } else if rem.starts_with("&#x") {
939 let mut valid = false;
941 if let Some(endpos) = rem.find(';') {
942 let hextxt = &rem[3..endpos];
943 if let Ok(hexval) = u32::from_str_radix(hextxt, 16)
944 && let Some(ch) = char::from_u32(hexval)
945 {
946 unescaped.push(ch);
947 rem = &rem[endpos + 1..];
948 valid = true;
949 }
950 }
951 if !valid {
952 self.optional_error(ArxmlParserError::InvalidXmlEntity {
953 input: input.to_owned(),
954 })?;
955 unescaped.push('&');
956 rem = &rem[1..];
957 }
958 } else if rem.starts_with("&#") {
959 let mut valid = false;
961 if let Some(endpos) = rem.find(';') {
962 let numtxt = &rem[2..endpos];
963 if let Ok(val) = u32::from_str(numtxt)
964 && let Some(ch) = char::from_u32(val)
965 {
966 unescaped.push(ch);
967 rem = &rem[endpos + 1..];
968 valid = true;
969 }
970 }
971 if !valid {
972 self.optional_error(ArxmlParserError::InvalidXmlEntity {
973 input: input.to_owned(),
974 })?;
975 unescaped.push('&');
976 rem = &rem[1..];
977 }
978 } else {
979 self.optional_error(ArxmlParserError::InvalidXmlEntity {
980 input: input.to_owned(),
981 })?;
982 unescaped.push('&');
983 rem = &rem[1..];
984 }
985 }
986 unescaped.push_str(rem);
987
988 Ok(Cow::Owned(unescaped))
989 } else {
990 Ok(Cow::Borrowed(input))
991 }
992 }
993
994 pub(crate) fn get_fileversion(&self) -> AutosarVersion {
995 self.fileversion
996 }
997
998 fn verify_end_of_input(&mut self, lexer: &mut ArxmlLexer) -> Result<(), AutosarDataError> {
999 let (_, next_event) = lexer.next()?;
1000 if let ArxmlEvent::EndOfFile = next_event {
1001 Ok(())
1002 } else {
1003 self.optional_error(ArxmlParserError::AdditionalDataError)?;
1004 Ok(())
1005 }
1006 }
1007
1008 pub(crate) fn check_arxml_header(&mut self) -> bool {
1010 let mut lexer = ArxmlLexer::new(self.buffer, self.filename.clone());
1011
1012 if let Ok(ArxmlEvent::ArxmlHeader(_)) = self.next(&mut lexer) {
1013 let mut arxmlevent = self.next(&mut lexer);
1015 while let Ok(ArxmlEvent::Comment(..)) = arxmlevent {
1016 arxmlevent = self.next(&mut lexer);
1017 }
1018 if let Ok(ArxmlEvent::BeginElement(elemname, attributes_text)) = arxmlevent
1019 && let Ok(ElementName::Autosar) = ElementName::from_bytes(elemname)
1020 && let Ok(attributes) = self.parse_attribute_text(ElementType::ROOT, attributes_text)
1021 && self.parse_file_header(&attributes).is_ok()
1022 {
1023 return true;
1025 }
1026 }
1027
1028 false
1029 }
1030}
1031
1032fn trim_byte_string(input: &[u8]) -> &[u8] {
1033 let mut len = input.len();
1034 if len > 0 {
1035 while input[len - 1].is_ascii_whitespace() {
1036 len -= 1;
1037 }
1038 let start = input.iter().position(|c| !c.is_ascii_whitespace()).unwrap_or(len);
1039 &input[start..len]
1040 } else {
1041 input
1042 }
1043}
1044
1045#[cfg(test)]
1046mod test {
1047 use crate::parser::*;
1048 use crate::*;
1049
1050 fn test_helper(buffer: &[u8], target_error: std::mem::Discriminant<ArxmlParserError>, optional: bool) {
1051 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), buffer, true);
1052 let result = parser.parse_arxml();
1053 if let Err(AutosarDataError::ParserError { source, .. }) = result {
1054 println!("Error result: {source:?}");
1055 assert_eq!(
1056 std::mem::discriminant(&source),
1057 target_error,
1058 "Did not get the expected parser error"
1059 );
1060 } else {
1061 panic!("Did not get any parser error when one was expected");
1062 }
1063
1064 if optional {
1065 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), buffer, false);
1066 let _result = parser.parse_arxml();
1067 if let Some(AutosarDataError::ParserError { source, .. }) = parser.warnings.first() {
1068 println!("Warnings result: {source:?}");
1069 assert_eq!(
1070 std::mem::discriminant(source),
1071 target_error,
1072 "Did not get the expected parser error"
1073 );
1074 } else {
1075 panic!("Did not get a parser warning");
1076 }
1077 }
1078 }
1079
1080 const INVALID_HEADER_1: &str = "BLA BLA bla";
1081 const INVALID_HEADER_2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1082 <something>"#;
1083 const INVALID_HEADER_3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1084 <AUTOSAR xsi:schemaLocation="nonsense" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">"#;
1085 const INVALID_HEADER_4: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1086 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00049.xsd" xmlns="http://other" xmlns:xsi="http://other">"#;
1087
1088 #[test]
1089 fn test_invalid_header() {
1090 test_helper(
1091 INVALID_HEADER_1.as_bytes(),
1092 std::mem::discriminant(&ArxmlParserError::InvalidArxmlFileHeader),
1093 false,
1094 );
1095 test_helper(
1096 INVALID_HEADER_2.as_bytes(),
1097 std::mem::discriminant(&ArxmlParserError::InvalidArxmlFileHeader),
1098 false,
1099 );
1100 test_helper(
1101 INVALID_HEADER_3.as_bytes(),
1102 std::mem::discriminant(&ArxmlParserError::InvalidArxmlFileHeader),
1103 false,
1104 );
1105 test_helper(
1106 INVALID_HEADER_4.as_bytes(),
1107 std::mem::discriminant(&ArxmlParserError::InvalidArxmlFileHeader),
1108 false,
1109 );
1110 }
1111
1112 const HDR_SINGLE_QUOTE: &str = r#"<?xml version='1.0' encoding='utf-8'?>
1113 <AUTOSAR xsi:schemaLocation='http://autosar.org/schema/r4.0 autosar_4-3-0.xsd' xmlns='http://autosar.org/schema/r4.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
1114 </AUTOSAR>"#;
1115
1116 #[test]
1117 fn single_quotes_in_header() {
1118 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), HDR_SINGLE_QUOTE.as_bytes(), true);
1119 let result = parser.parse_arxml();
1120 assert!(result.is_ok());
1121 }
1122
1123 const SCHEMA_VERSION_LC: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1124 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 autosar_4-3-0.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1125 </AUTOSAR>"#;
1126 const INVALID_VERSION_4_3_1: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1127 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-3-1.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1128 </AUTOSAR>"#;
1129 const INVALID_VERSION_4_4_0: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1130 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-4-0.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1131 </AUTOSAR>"#;
1132 const INVALID_VERSION_4_5_0: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1133 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-5-0.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1134 </AUTOSAR>"#;
1135
1136 #[test]
1137 fn test_invalid_version() {
1138 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), SCHEMA_VERSION_LC.as_bytes(), true);
1139 let result = parser.parse_arxml();
1140 assert!(result.is_ok());
1141
1142 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidAutosarVersion {
1143 input_verstring: "".to_string(),
1144 replacement: AutosarVersion::Autosar_00044,
1145 });
1146 test_helper(INVALID_VERSION_4_3_1.as_bytes(), discriminant, true);
1147 test_helper(INVALID_VERSION_4_4_0.as_bytes(), discriminant, true);
1148 test_helper(INVALID_VERSION_4_5_0.as_bytes(), discriminant, true);
1149 }
1150
1151 const UNKNOWN_VERSION: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1152 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_something_else.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1153 </AUTOSAR>"#;
1154
1155 #[test]
1156 fn test_unknown_version() {
1157 let discriminant = std::mem::discriminant(&ArxmlParserError::UnknownAutosarVersion {
1158 input_verstring: "".to_string(),
1159 });
1160 test_helper(UNKNOWN_VERSION.as_bytes(), discriminant, true);
1161 }
1162
1163 const UNEXPECTED_XML_FILE_HEADER: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1164 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1165 <?xml version="1.0" encoding="utf-8"?>
1166 </AUTOSAR>"#;
1167
1168 #[test]
1169 fn test_unexpected_xml_file_header() {
1170 let discriminant = std::mem::discriminant(&ArxmlParserError::UnexpectedXmlFileHeader {
1171 element: ElementName::Autosar,
1172 });
1173 test_helper(UNEXPECTED_XML_FILE_HEADER.as_bytes(), discriminant, true);
1174 }
1175
1176 const INCORRECT_BEGIN_ELEMENT: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1177 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1178 <ELEMENT>"#;
1179
1180 #[test]
1181 fn test_incorrect_begin_element() {
1182 let discriminant = std::mem::discriminant(&ArxmlParserError::IncorrectBeginElement {
1183 element: ElementName::Autosar,
1184 sub_element: ElementName::Autosar,
1185 });
1186 test_helper(INCORRECT_BEGIN_ELEMENT.as_bytes(), discriminant, false);
1187 }
1188
1189 const INVALID_BEGIN_ELEMENT: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1190 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1191 <NOT_AN_AUTOSAR_ELEMENT>"#;
1192
1193 #[test]
1194 fn test_invalid_begin_element() {
1195 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidBeginElement {
1196 element: ElementName::Autosar,
1197 invalid_element: "".to_string(),
1198 });
1199 test_helper(INVALID_BEGIN_ELEMENT.as_bytes(), discriminant, false);
1200 }
1201
1202 const INCORRECT_END_ELEMENT: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1203 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1204 <AR-PACKAGES></AUTOSAR>"#;
1205
1206 #[test]
1207 fn test_incorrect_end_element() {
1208 let discriminant = std::mem::discriminant(&ArxmlParserError::IncorrectEndElement {
1209 element: ElementName::Autosar,
1210 other_element: ElementName::Autosar,
1211 });
1212 test_helper(INCORRECT_END_ELEMENT.as_bytes(), discriminant, false);
1213 }
1214
1215 const INVALID_END_ELEMENT: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1216 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1217 <AR-PACKAGES></NOT_AN_AUTOSAR_ELEMENT>"#;
1218
1219 #[test]
1220 fn test_invalid_end_element() {
1221 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidEndElement {
1222 parent_element: ElementName::Autosar,
1223 invalid_element: "".to_string(),
1224 });
1225 test_helper(INVALID_END_ELEMENT.as_bytes(), discriminant, false);
1226 }
1227
1228 const ELEMENT_VERSION_ERROR: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1229 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-0-1.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1230 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>TestPackage</SHORT-NAME><ELEMENTS><DIAGNOSTIC-ACCESS-PERMISSION>"#;
1231
1232 #[test]
1233 fn test_element_version_error() {
1234 let discriminant = std::mem::discriminant(&ArxmlParserError::ElementVersionError {
1235 element: ElementName::Autosar,
1236 sub_element: ElementName::Autosar,
1237 version: AutosarVersion::Autosar_00050,
1238 });
1239 test_helper(ELEMENT_VERSION_ERROR.as_bytes(), discriminant, false);
1240 }
1241
1242 const CHOICE_CONFLICT: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1243 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1244 <AR-PACKAGES>
1245 <AR-PACKAGE>
1246 <SHORT-NAME>base</SHORT-NAME>
1247 <ELEMENTS>
1248 <DIAGNOSTIC-CONTRIBUTION-SET>
1249 <SHORT-NAME>dcs</SHORT-NAME>
1250 <COMMON-PROPERTIES>
1251 <DIAGNOSTIC-COMMON-PROPS-VARIANTS>
1252 <DIAGNOSTIC-COMMON-PROPS-CONDITIONAL>
1253 <DEBOUNCE-ALGORITHM-PROPSS>
1254 <DIAGNOSTIC-DEBOUNCE-ALGORITHM-PROPS>
1255 <SHORT-NAME>props</SHORT-NAME>
1256 <DEBOUNCE-ALGORITHM>
1257 <DIAG-EVENT-DEBOUNCE-COUNTER-BASED>
1258 <SHORT-NAME>abc</SHORT-NAME>
1259 </DIAG-EVENT-DEBOUNCE-COUNTER-BASED>
1260 <DIAG-EVENT-DEBOUNCE-TIME-BASED>
1261 <SHORT-NAME>def</SHORT-NAME>
1262 </DIAG-EVENT-DEBOUNCE-TIME-BASED>"#;
1263
1264 #[test]
1265 fn test_choice_conflict() {
1266 let discriminant = std::mem::discriminant(&ArxmlParserError::ElementChoiceConflict {
1267 element: ElementName::Autosar,
1268 sub_element: ElementName::Autosar,
1269 });
1270 test_helper(CHOICE_CONFLICT.as_bytes(), discriminant, true);
1271 }
1272
1273 const TOO_MANY_SUBELEMENTS: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1274 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1275 <AR-PACKAGES>
1276 <AR-PACKAGE>
1277 <SHORT-NAME>base</SHORT-NAME>
1278 <SHORT-NAME>base</SHORT-NAME>"#;
1279
1280 #[test]
1281 fn test_too_many_sub_elements() {
1282 let discriminant = std::mem::discriminant(&ArxmlParserError::TooManySubElements {
1283 element: ElementName::Autosar,
1284 sub_element: ElementName::Autosar,
1285 });
1286 test_helper(TOO_MANY_SUBELEMENTS.as_bytes(), discriminant, true);
1287 }
1288
1289 const REQUIRED_SUB_ELEMENT_MISSING: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1290 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1291 <AR-PACKAGES>
1292 <AR-PACKAGE></AR-PACKAGE>"#;
1293
1294 #[test]
1295 fn test_required_sub_element_missing() {
1296 let discriminant = std::mem::discriminant(&ArxmlParserError::RequiredSubelementMissing {
1297 element: ElementName::Autosar,
1298 sub_element: ElementName::Autosar,
1299 });
1300 test_helper(REQUIRED_SUB_ELEMENT_MISSING.as_bytes(), discriminant, false);
1301 }
1302
1303 const UNKNOWN_ATTRIBUTE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1304 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1305 <AR-PACKAGES UnknownAttribute="value">
1306 </AR-PACKAGES></AUTOSAR>"#;
1307 const UNKNOWN_ATTRIBUTE_2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1308 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1309 <AR-PACKAGES DEST="value">
1310 </AR-PACKAGES></AUTOSAR>"#;
1311
1312 #[test]
1313 fn test_unknown_attribute() {
1314 let discriminant = std::mem::discriminant(&ArxmlParserError::UnknownAttributeError {
1315 element: ElementName::Autosar,
1316 attribute: "".to_string(),
1317 });
1318 test_helper(UNKNOWN_ATTRIBUTE.as_bytes(), discriminant, true);
1319 let discriminant = std::mem::discriminant(&ArxmlParserError::UnknownAttributeError {
1320 element: ElementName::Autosar,
1321 attribute: "DEST".to_string(),
1322 });
1323 test_helper(UNKNOWN_ATTRIBUTE_2.as_bytes(), discriminant, true);
1324 }
1325
1326 const REQUIRED_ATTRIBUTE_MISSING: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1327 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0">
1328 </AUTOSAR>"#;
1329
1330 #[test]
1331 fn test_required_attribute_missing() {
1332 let discriminant = std::mem::discriminant(&ArxmlParserError::RequiredAttributeMissing {
1333 element: ElementName::Autosar,
1334 attribute: AttributeName::Accesskey,
1335 });
1336 test_helper(REQUIRED_ATTRIBUTE_MISSING.as_bytes(), discriminant, true);
1337 }
1338
1339 const CHARACTER_CONTENT_FORBIDDEN: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1340 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1341 abcdef"#;
1342
1343 #[test]
1344 fn test_character_content_forbidden() {
1345 let discriminant = std::mem::discriminant(&ArxmlParserError::CharacterContentForbidden {
1346 element: ElementName::Autosar,
1347 });
1348 test_helper(CHARACTER_CONTENT_FORBIDDEN.as_bytes(), discriminant, false);
1349 }
1350
1351 const WRONG_ENUM_ITEM_VERSION: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1352 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00044.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1353 <AR-PACKAGES>
1354 <AR-PACKAGE>
1355 <SHORT-NAME>base</SHORT-NAME>
1356 <ELEMENTS>
1357 <SYSTEM>
1358 <SHORT-NAME>System</SHORT-NAME>
1359 <FIBEX-ELEMENTS>
1360 <FIBEX-ELEMENT-REF-CONDITIONAL>
1361 <FIBEX-ELEMENT-REF DEST="SERVICE-INSTANCE-COLLECTION-SET">"#;
1362
1363 #[test]
1364 fn test_enum_item_version() {
1365 let discriminant = std::mem::discriminant(&ArxmlParserError::EnumItemVersionError {
1366 element: ElementName::Autosar,
1367 enum_item: EnumItem::Aa,
1368 version: AutosarVersion::Autosar_00050,
1369 });
1370 test_helper(WRONG_ENUM_ITEM_VERSION.as_bytes(), discriminant, false);
1371 }
1372
1373 const UNKNOWN_ENUM_ITEM: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1374 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1375 <AR-PACKAGES>
1376 <AR-PACKAGE>
1377 <SHORT-NAME>base</SHORT-NAME>
1378 <ELEMENTS>
1379 <SYSTEM>
1380 <SHORT-NAME>System</SHORT-NAME>
1381 <FIBEX-ELEMENTS>
1382 <FIBEX-ELEMENT-REF-CONDITIONAL>
1383 <FIBEX-ELEMENT-REF DEST="invalid_value_for_the_test">"#;
1384
1385 #[test]
1386 fn test_unknown_enum_item() {
1387 let discriminant = std::mem::discriminant(&ArxmlParserError::UnknownEnumItem { value: "".to_string() });
1388 test_helper(UNKNOWN_ENUM_ITEM.as_bytes(), discriminant, false);
1389 }
1390
1391 const INVALID_ENUM_ITEM: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1392 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1393 <AR-PACKAGES>
1394 <AR-PACKAGE>
1395 <SHORT-NAME>base</SHORT-NAME>
1396 <ELEMENTS>
1397 <SYSTEM>
1398 <SHORT-NAME>System</SHORT-NAME>
1399 <FIBEX-ELEMENTS>
1400 <FIBEX-ELEMENT-REF-CONDITIONAL>
1401 <FIBEX-ELEMENT-REF DEST="default">"#;
1402
1403 #[test]
1404 fn test_invalid_enum_item() {
1405 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidEnumItem {
1406 element: ElementName::Abs,
1407 item: EnumItem::Aa,
1408 });
1409 test_helper(INVALID_ENUM_ITEM.as_bytes(), discriminant, false);
1410 }
1411
1412 const STRING_VALUE_TOO_LONG: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1413 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1414 <AR-PACKAGES><AR-PACKAGE>
1415 <SHORT-NAME>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</SHORT-NAME>
1416 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
1417
1418 #[test]
1419 fn test_string_value_too_long() {
1420 let discriminant = std::mem::discriminant(&ArxmlParserError::StringValueTooLong {
1421 value: "".to_string(),
1422 length: 1,
1423 });
1424 test_helper(STRING_VALUE_TOO_LONG.as_bytes(), discriminant, true);
1425 }
1426
1427 const REGEX_MATCH_ERROR: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1428 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1429 <AR-PACKAGES><AR-PACKAGE>
1430 <SHORT-NAME>0a</SHORT-NAME>
1431 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
1432
1433 #[test]
1434 fn test_regex_match_error() {
1435 let discriminant = std::mem::discriminant(&ArxmlParserError::RegexMatchError {
1436 value: "".to_string(),
1437 regex: "".to_string(),
1438 });
1439 test_helper(REGEX_MATCH_ERROR.as_bytes(), discriminant, true);
1440 }
1441
1442 const UTF8_ERROR: &[u8] = b"<?xml version=\"1.0\" encoding=\"utf-8\"?>
1443 <AUTOSAR xsi:schemaLocation=\"http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd\" xmlns=\"http://autosar.org/schema/r4.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">
1444 <AR-PACKAGES><AR-PACKAGE S=\"\xff\xff\">";
1445
1446 #[test]
1447 fn test_utf8_error() {
1448 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), UTF8_ERROR, true);
1449 let result = parser.parse_arxml();
1450 assert!(
1451 matches!(
1452 result,
1453 Err(AutosarDataError::ParserError {
1454 source: ArxmlParserError::Utf8Error { .. },
1455 ..
1456 })
1457 ),
1458 "Did not get the expected parser error"
1459 );
1460
1461 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), UTF8_ERROR, false);
1462 let _ = parser.parse_arxml();
1463 let warning = parser.warnings.first();
1464 assert!(
1465 matches!(
1466 warning,
1467 Some(AutosarDataError::ParserError {
1468 source: ArxmlParserError::Utf8Error { .. },
1469 ..
1470 })
1471 ),
1472 "Did not get the expected parser warning"
1473 );
1474 }
1475
1476 const UNEXPECTED_END_OF_FILE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1477 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">"#;
1478
1479 #[test]
1480 fn test_unexpected_end_of_file() {
1481 let discriminant = std::mem::discriminant(&ArxmlParserError::UnexpectedEndOfFile {
1482 element: ElementName::Autosar,
1483 });
1484 test_helper(UNEXPECTED_END_OF_FILE.as_bytes(), discriminant, false);
1485 }
1486
1487 const INVALID_NUMBER: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1488 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1489 <AR-PACKAGES><AR-PACKAGE>
1490 <SHORT-NAME>base</SHORT-NAME>
1491 <ELEMENTS><I-SIGNAL-I-PDU>
1492 <SHORT-NAME>Pdu</SHORT-NAME>
1493 <I-PDU-TIMING-SPECIFICATIONS><I-PDU-TIMING><TRANSMISSION-MODE-DECLARATION><TRANSMISSION-MODE-TRUE-TIMING><CYCLIC-TIMING>
1494 <TIME-PERIOD><TOLERANCE><ABSOLUTE-TOLERANCE><ABSOLUTE>not a number</ABSOLUTE>"#;
1495
1496 #[test]
1497 fn test_invalid_number() {
1498 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidNumber { input: "".to_string() });
1499 test_helper(INVALID_NUMBER.as_bytes(), discriminant, true);
1500 }
1501
1502 const ADDITIONAL_DATA: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1503 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1504 </AUTOSAR>
1505 <extra>"#;
1506
1507 #[test]
1508 fn test_additional_data_error() {
1509 let discriminant = std::mem::discriminant(&ArxmlParserError::AdditionalDataError);
1510 test_helper(ADDITIONAL_DATA.as_bytes(), discriminant, true);
1511 }
1512
1513 #[test]
1514 fn unescape_entities() {
1515 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), &[], true);
1516 let result = parser
1517 .unescape_string("&&<FOO>""'  end")
1518 .unwrap();
1519 assert_eq!(&result, r#"&&<FOO>""' end"#);
1520 let result = parser.unescape_string("&&>FOO<""'end");
1521 assert!(result.is_err());
1522 let result = parser.unescape_string("&#abcde;");
1524 assert!(result.is_err());
1525 let result = parser.unescape_string("�");
1527 assert!(result.is_err());
1528 }
1529
1530 const PARSER_TEST_DATA: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1531 <!--comment-->
1532 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1533 <!--comment-->
1534 <AR-PACKAGES>
1535 <AR-PACKAGE>
1536 <SHORT-NAME>base</SHORT-NAME>
1537 <ELEMENTS>
1538 <SYSTEM UUID="12345678" S="some string" T="2022-01-31T13:00:59Z">
1539 <SHORT-NAME>System</SHORT-NAME>
1540 <FIBEX-ELEMENTS>
1541 <FIBEX-ELEMENT-REF-CONDITIONAL>
1542 <FIBEX-ELEMENT-REF DEST="I-SIGNAL-I-PDU">/base/Pdu</FIBEX-ELEMENT-REF>
1543 </FIBEX-ELEMENT-REF-CONDITIONAL>
1544 </FIBEX-ELEMENTS>
1545 </SYSTEM>
1546 <I-SIGNAL-I-PDU>
1547 <SHORT-NAME>Pdu</SHORT-NAME>
1548 <I-PDU-TIMING-SPECIFICATIONS>
1549 <I-PDU-TIMING>
1550 <TRANSMISSION-MODE-DECLARATION>
1551 <TRANSMISSION-MODE-TRUE-TIMING>
1552 <CYCLIC-TIMING>
1553 <TIME-PERIOD>
1554 <TOLERANCE>
1555 <ABSOLUTE-TOLERANCE>
1556 <ABSOLUTE>1.0</ABSOLUTE>
1557 </ABSOLUTE-TOLERANCE>
1558 </TOLERANCE>
1559 </TIME-PERIOD>
1560 </CYCLIC-TIMING>
1561 </TRANSMISSION-MODE-TRUE-TIMING>
1562 </TRANSMISSION-MODE-DECLARATION>
1563 </I-PDU-TIMING>
1564 </I-PDU-TIMING-SPECIFICATIONS>
1565 </I-SIGNAL-I-PDU>
1566 </ELEMENTS>
1567 </AR-PACKAGE>
1568 </AR-PACKAGES>
1569 </AUTOSAR>
1570 "#;
1571
1572 #[test]
1573 fn test_basic_functionality() {
1574 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), PARSER_TEST_DATA.as_bytes(), true);
1575 let result = parser.parse_arxml();
1576 assert!(result.is_ok());
1577 }
1578
1579 const PARSER_TEST_RELATIVE_REFERENCE_BASE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1580 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1581 <AR-PACKAGES>
1582 <AR-PACKAGE>
1583 <SHORT-NAME>base</SHORT-NAME>
1584 <REFERENCE-BASES>
1585 <REFERENCE-BASE>
1586 <SHORT-LABEL>default</SHORT-LABEL>
1587 <PACKAGE-REF DEST="AR-PACKAGE">/base</PACKAGE-REF>
1588 </REFERENCE-BASE>
1589 </REFERENCE-BASES>
1590 <ELEMENTS>
1591 <SYSTEM>
1592 <SHORT-NAME>System</SHORT-NAME>
1593 <FIBEX-ELEMENTS>
1594 <FIBEX-ELEMENT-REF-CONDITIONAL>
1595 <FIBEX-ELEMENT-REF DEST="I-SIGNAL-I-PDU" BASE="default">Pdu</FIBEX-ELEMENT-REF>
1596 </FIBEX-ELEMENT-REF-CONDITIONAL>
1597 </FIBEX-ELEMENTS>
1598 </SYSTEM>
1599 <I-SIGNAL-I-PDU>
1600 <SHORT-NAME>Pdu</SHORT-NAME>
1601 </I-SIGNAL-I-PDU>
1602 </ELEMENTS>
1603 </AR-PACKAGE>
1604 </AR-PACKAGES>
1605 </AUTOSAR>
1606 "#;
1607
1608 #[test]
1609 fn parse_reference_base_and_relative_reference() {
1610 let mut parser = ArxmlParser::new(
1611 PathBuf::from("test_buffer.arxml"),
1612 PARSER_TEST_RELATIVE_REFERENCE_BASE.as_bytes(),
1613 true,
1614 );
1615
1616 let result = parser.parse_arxml();
1617 assert!(result.is_ok());
1618
1619 assert_eq!(parser.reference_bases.len(), 1);
1620 let (base_label, base_info) = &parser.reference_bases[0];
1621 assert_eq!(base_label, "default");
1622 assert_eq!(base_info.package_ref, "/base");
1623 assert_eq!(base_info.owner_package_path, "/base");
1624 assert_eq!(base_info.package_ref_base, None);
1625
1626 assert_eq!(parser.references.len(), 2);
1627 assert!(parser.references.iter().any(|(refpath, _, _)| refpath == "/base"));
1628 assert!(parser.references.iter().any(|(refpath, _, _)| refpath == "Pdu"));
1629 }
1630
1631 const EMPTY_CHARACTER_DATA: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1632 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1633 <AR-PACKAGES>
1634 <AR-PACKAGE UUID="">
1635 <SHORT-NAME>x</SHORT-NAME>
1636 </AR-PACKAGE>
1637 </AR-PACKAGES>
1638 </AUTOSAR>
1639 "#;
1640
1641 #[test]
1642 fn test_empty_character_data() {
1643 let mut parser = ArxmlParser::new(
1644 PathBuf::from("test_buffer.arxml"),
1645 EMPTY_CHARACTER_DATA.as_bytes(),
1646 true,
1647 );
1648 let result = parser.parse_arxml();
1649 assert!(result.is_ok());
1650 }
1651
1652 #[test]
1653 fn chardata_utf8_error() {
1654 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), b"", true);
1655 let mut parser_permissive = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), b"", false);
1656
1657 let pattern_spec = CharacterDataSpec::Pattern {
1659 check_fn: |_| true,
1660 regex: "",
1661 max_length: Some(2),
1662 };
1663 let result = parser.parse_character_data(b"ab", &pattern_spec);
1664 assert!(result.is_ok());
1665
1666 let result = parser.parse_character_data(&[0xff], &pattern_spec);
1668 assert!(result.is_err());
1669
1670 let result = parser_permissive.parse_character_data(&[0xff], &pattern_spec);
1672 assert!(result.is_ok());
1673
1674 let string_spec = CharacterDataSpec::String {
1676 max_length: Some(2),
1677 preserve_whitespace: false,
1678 };
1679 let result = parser.parse_character_data(b"ab", &string_spec);
1680 assert!(result.is_ok());
1681
1682 let result = parser.parse_character_data(b"abc", &string_spec);
1684 assert!(result.is_err());
1685
1686 let result = parser.parse_character_data(&[0xff], &string_spec);
1688 assert!(result.is_err());
1689
1690 let int_spec = CharacterDataSpec::UnsignedInteger;
1692 let result = parser.parse_character_data(b"123", &int_spec);
1693 assert!(result.is_ok());
1694
1695 let result = parser.parse_character_data(b"abc", &int_spec);
1697 assert!(result.is_err());
1698
1699 let result = parser.parse_character_data(&[0xff], &int_spec);
1701 assert!(result.is_err());
1702
1703 let float_spec = CharacterDataSpec::Float;
1705 let result = parser.parse_character_data(b"1.0", &float_spec);
1706 assert!(result.is_ok());
1707
1708 let result = parser.parse_character_data(b"abc", &float_spec);
1710 assert!(result.is_err());
1711
1712 let result = parser.parse_character_data(&[0xff], &float_spec);
1714 assert!(result.is_err());
1715 }
1716
1717 #[test]
1718 fn test_check_arxml_header() {
1719 let buffer = "abcde".as_bytes();
1720 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1721 assert!(!parser.check_arxml_header());
1722
1723 let buffer = r#"<?xml version="1.0" encoding="utf-8"?>abcde"#.as_bytes();
1724 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1725 assert!(!parser.check_arxml_header());
1726
1727 let buffer = r#"<?xml version="1.0" encoding="utf-8"?><abcde>"#.as_bytes();
1728 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1729 assert!(!parser.check_arxml_header());
1730
1731 let buffer = r#"<?xml version="1.0" encoding="utf-8"?><AUTOSAR abcde="abcde">"#.as_bytes();
1732 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1733 assert!(!parser.check_arxml_header());
1734
1735 let buffer = r#"<?xml version="1.0" encoding="utf-8"?>
1736<AUTOSAR>"#
1737 .as_bytes();
1738 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1739 assert!(!parser.check_arxml_header());
1740
1741 let buffer = r#"<?xml version="1.0" encoding="utf-8"?>
1742<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 abcdef" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">"#.as_bytes();
1743 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1744 assert!(!parser.check_arxml_header());
1745
1746 let buffer = r#"<?xml version="1.0" encoding="utf-8"?>
1747<AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">"#.as_bytes();
1748 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1749 assert!(parser.check_arxml_header());
1750 }
1751
1752 #[test]
1753 fn parse_attribute_text() {
1754 let mut parser = ArxmlParser::new(PathBuf::from("test"), &[], true);
1755 let etype_arpackage = ElementType::ROOT
1757 .find_sub_element(ElementName::ArPackages, u32::MAX)
1758 .unwrap()
1759 .0
1760 .find_sub_element(ElementName::ArPackage, u32::MAX)
1761 .unwrap()
1762 .0;
1763
1764 let result = parser.parse_attribute_text(etype_arpackage, br#" "#);
1766 assert!(result.is_ok());
1767 let value = result.unwrap();
1768 assert!(value.is_empty());
1769
1770 let result = parser.parse_attribute_text(etype_arpackage, br#" abc "#);
1772 assert!(result.is_err());
1773
1774 let result = parser.parse_attribute_text(etype_arpackage, br#"abc="#);
1776 assert!(result.is_err());
1777
1778 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID="#);
1780 assert!(result.is_err());
1781
1782 let value = parser
1784 .parse_attribute_text(etype_arpackage, br#"UUID="12345678""#)
1785 .unwrap();
1786 assert_eq!(value.len(), 1);
1787 assert_eq!(value[0].attrname, AttributeName::Uuid);
1788
1789 let value = parser
1791 .parse_attribute_text(etype_arpackage, br#"UUID="12345678" "#)
1792 .unwrap();
1793 assert_eq!(value.len(), 1);
1794
1795 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID="12345678" abc "#);
1797 assert!(result.is_err());
1798
1799 let value = parser
1801 .parse_attribute_text(etype_arpackage, br#"UUID='12345678'"#)
1802 .unwrap();
1803 assert_eq!(value.len(), 1);
1804
1805 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID='12345678"#);
1807 assert!(result.is_err());
1808
1809 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID='12345678""#);
1811 assert!(result.is_err());
1812
1813 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID="12345678"T="2024-01-01""#);
1815 assert!(result.is_err());
1816
1817 let value = parser
1819 .parse_attribute_text(etype_arpackage, br#"UUID="12345678" T="2024-01-01""#)
1820 .unwrap();
1821 assert_eq!(value.len(), 2);
1822
1823 let value = parser
1825 .parse_attribute_text(etype_arpackage, br#"UUID="12345678" T="2024-01-01""#)
1826 .unwrap();
1827 assert_eq!(value.len(), 2);
1828
1829 let value = parser
1831 .parse_attribute_text(etype_arpackage, br#" UUID="12345678" T="2024-01-01""#)
1832 .unwrap();
1833 assert_eq!(value.len(), 2);
1834 }
1835}