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::path::PathBuf;
8use std::str::FromStr;
9use std::str::Utf8Error;
10use thiserror::Error;
11
12use crate::WeakAutosarModel;
13use crate::lexer::{ArxmlEvent, ArxmlLexer};
14use crate::{
15 Attribute, AutosarDataError, CharacterData, Element, ElementContent, ElementOrModel, ElementRaw, WeakElement,
16};
17
18#[derive(Debug, Error, PartialEq)]
19#[non_exhaustive]
20pub enum ArxmlParserError {
22 #[error("Invalid arxml file: bad file header")]
24 InvalidArxmlFileHeader,
25
26 #[error("Unexpected XML file header found inside ARXML data")]
28 UnexpectedXmlFileHeader {
29 element: ElementName,
31 },
32
33 #[error("Unknown Autosar xsd file {input_verstring} referenced in the file header")]
35 UnknownAutosarVersion {
36 input_verstring: String,
38 },
39
40 #[error("Invalid Autosar xsd file {input_verstring} referenced in the file header should be replaced with {}", .replacement.filename())]
42 InvalidAutosarVersion {
43 input_verstring: String,
45 replacement: AutosarVersion,
47 },
48
49 #[error("Encountered unexpected child element {sub_element} inside element {element}")]
51 IncorrectBeginElement {
52 element: ElementName,
54 sub_element: ElementName,
56 },
57
58 #[error(
60 "Encountered invalid child element {invalid_element} inside parent element {element}. {invalid_element} is not a known Autosar element."
61 )]
62 InvalidBeginElement {
63 element: ElementName,
65 invalid_element: String,
67 },
68
69 #[error("Encountered the closing tag for element {other_element}, but element {element} was open.")]
71 IncorrectEndElement {
72 element: ElementName,
74 other_element: ElementName,
76 },
77
78 #[error(
80 "Encountered invalid end tag for element {invalid_element} inside parent element {parent_element}. {invalid_element} is not a known Autosar element."
81 )]
82 InvalidEndElement {
83 parent_element: ElementName,
85 invalid_element: String,
87 },
88
89 #[error("Multiple conflicting sub elements have been added to element {element}. The latest was {sub_element}.")]
91 ElementChoiceConflict {
92 element: ElementName,
94 sub_element: ElementName,
96 },
97
98 #[error("Element {sub_element} exists in {element}, but is not allowed in {version}")]
100 ElementVersionError {
101 element: ElementName,
103 sub_element: ElementName,
105 version: AutosarVersion,
107 },
108
109 #[error("Only one {sub_element} is allowed inside {element}, but another occurrence was found")]
111 TooManySubElements {
112 element: ElementName,
114 sub_element: ElementName,
116 },
117
118 #[error("The required sub element {sub_element} was not found in element {element}")]
120 RequiredSubelementMissing {
121 element: ElementName,
123 sub_element: ElementName,
125 },
126
127 #[error("Could not parse the attribute text \"{attribute_text}\" in element {element}")]
129 AttributeValueError {
130 element: ElementName,
132 attribute_text: String,
134 },
135
136 #[error("Element {element} contains unknown attribute {attribute}")]
138 UnknownAttributeError {
139 element: ElementName,
141 attribute: String,
143 },
144
145 #[error("Attribute {attribute} exists in element {element}, but is not allowed in {version}")]
147 AttributeVersionError {
148 element: ElementName,
150 attribute: AttributeName,
152 version: AutosarVersion,
154 },
155
156 #[error("Attribute {attribute} is required in element {element}, but was not found")]
158 RequiredAttributeMissing {
159 element: ElementName,
161 attribute: AttributeName,
163 },
164
165 #[error("The attribute {attribute} occurs multiple times in the element {element}")]
167 DuplicateAttributeError {
168 attribute: AttributeName,
170 element: ElementName,
172 },
173
174 #[error("Character content found, which is not allowed inside element {element}")]
176 CharacterContentForbidden {
177 element: ElementName,
179 },
180
181 #[error("enum item {enum_item} is a valid value in element {element}, but is not allowed in {version}")]
183 EnumItemVersionError {
184 element: ElementName,
186 enum_item: EnumItem,
188 version: AutosarVersion,
190 },
191
192 #[error("string {value} is not a valid enum item")]
194 UnknownEnumItem {
195 value: String,
197 },
198
199 #[error("enum item {item} is not valid in element {element}")]
201 InvalidEnumItem {
202 element: ElementName,
204 item: EnumItem,
206 },
207
208 #[error("string value {value} is too long: max length is {length}")]
210 StringValueTooLong {
211 value: String,
213 length: usize,
215 },
216
217 #[error("string value {value} is not matched by the validation regex {regex}")]
219 RegexMatchError {
220 value: String,
222 regex: String,
224 },
225
226 #[error("could not convert value to utf-8: {source}")]
228 Utf8Error {
229 source: Utf8Error,
231 },
232
233 #[error("Unexpected end of file while parsing element {element}")]
235 UnexpectedEndOfFile {
236 element: ElementName,
238 },
239
240 #[error("Failed to parse {input} as a number")]
242 InvalidNumber {
243 input: String,
245 },
246
247 #[error("Additional data found in the input after the final </AUTOSAR> element")]
249 AdditionalDataError,
250
251 #[error("Invalid XML entity in {input}")]
253 InvalidXmlEntity {
254 input: String,
256 },
257
258 #[error("The SHORT-NAME of element {element} is empty")]
260 EmptyShortName {
261 element: ElementName,
263 },
264}
265
266pub(crate) struct ArxmlParser<'a> {
267 filename: PathBuf,
268 line: usize,
269 buffer: &'a [u8],
270 fileversion: AutosarVersion,
271 current_element: ElementName,
272 strict: bool,
273 version_compatibility: u32,
274 pub(crate) identifiables: Vec<(String, WeakElement)>,
275 pub(crate) references: Vec<(String, WeakElement, Option<String>)>,
276 pub(crate) warnings: Vec<AutosarDataError>,
277 standalone: Option<bool>,
278 pub(crate) model: WeakAutosarModel,
279}
280
281impl<'a> ArxmlParser<'a> {
282 pub(crate) fn new(filename: PathBuf, buffer: &'a [u8], strict: bool) -> Self {
283 Self {
284 filename,
285 line: 1,
286 buffer,
287 fileversion: AutosarVersion::Autosar_4_0_1, current_element: ElementName::Autosar,
289 strict,
290 version_compatibility: u32::MAX,
291 model: WeakAutosarModel::default(),
292 identifiables: Vec::new(),
293 references: Vec::new(),
294 warnings: Vec::new(),
295 standalone: None,
296 }
297 }
298
299 fn next<'b>(&mut self, lexer: &'b mut ArxmlLexer) -> Result<ArxmlEvent<'b>, AutosarDataError> {
300 let (line, event) = lexer.next()?;
301 self.line = line;
302 Ok(event)
303 }
304
305 pub(crate) fn error(&self, err: ArxmlParserError) -> AutosarDataError {
306 AutosarDataError::ParserError {
307 filename: self.filename.clone(),
308 line: self.line,
309 source: err,
310 }
311 }
312
313 pub(crate) fn optional_error(&mut self, err: ArxmlParserError) -> Result<(), AutosarDataError> {
314 let wrapped_err = AutosarDataError::ParserError {
315 filename: self.filename.clone(),
316 line: self.line,
317 source: err,
318 };
319 if self.strict {
320 Err(wrapped_err)
321 } else {
322 self.warnings.push(wrapped_err);
323 Ok(())
324 }
325 }
326
327 fn check_version(&mut self, item_version: u32, error: ArxmlParserError) -> Result<(), AutosarDataError> {
328 self.version_compatibility &= item_version;
329 if (self.fileversion as u32) & item_version == 0 {
330 self.optional_error(error)
331 } else {
332 Ok(())
333 }
334 }
335
336 pub(crate) fn parse_arxml(&mut self) -> Result<Element, AutosarDataError> {
338 let mut lexer = ArxmlLexer::new(self.buffer, self.filename.clone());
339
340 if let ArxmlEvent::ArxmlHeader(standalone) = self.next(&mut lexer)? {
341 self.standalone = standalone;
342 } else {
343 return Err(self.error(ArxmlParserError::InvalidArxmlFileHeader));
344 }
345
346 let mut stored_comment = None;
347 let mut token = self.next(&mut lexer)?;
348 while let ArxmlEvent::Comment(comment_bytes) = token {
349 stored_comment = Some(String::from_utf8_lossy(comment_bytes).into());
350 token = self.next(&mut lexer)?;
351 }
352
353 if let ArxmlEvent::BeginElement(elemname, attributes_text) = token
354 && let Ok(ElementName::Autosar) = ElementName::from_bytes(elemname)
355 {
356 let attributes = self.parse_attribute_text(ElementType::ROOT, attributes_text)?;
357 self.parse_file_header(&attributes)?;
358
359 let new_element = ElementRaw {
360 parent: ElementOrModel::None,
361 elemname: ElementName::Autosar,
362 elemtype: ElementType::ROOT,
363 content: SmallVec::new(),
364 attributes,
365 file_membership: None,
366 comment: stored_comment,
367 };
368 let path = Cow::from("");
369 let autosar_root_element = self.parse_element(new_element, path, &mut lexer)?;
370 self.verify_end_of_input(&mut lexer)?;
371
372 return Ok(autosar_root_element);
373 }
374 Err(self.error(ArxmlParserError::InvalidArxmlFileHeader))
375 }
376
377 fn parse_file_header(&mut self, attributes: &SmallVec<[Attribute; 1]>) -> Result<(), AutosarDataError> {
379 let attr_xmlns = attributes.iter().find(|attr| attr.attrname == AttributeName::xmlns);
380 let attr_xsi = attributes.iter().find(|attr| attr.attrname == AttributeName::xmlnsXsi);
381 let attr_schema = attributes
382 .iter()
383 .find(|attr| attr.attrname == AttributeName::xsiSchemalocation);
384 if let (
385 Some(Attribute {
386 content: CharacterData::String(xmlns),
387 ..
388 }),
389 Some(Attribute {
390 content: CharacterData::String(xsi),
391 ..
392 }),
393 Some(Attribute {
394 content: CharacterData::String(schema),
395 ..
396 }),
397 ) = (attr_xmlns, attr_xsi, attr_schema)
398 {
399 if xmlns != "http://autosar.org/schema/r4.0" || xsi != "http://www.w3.org/2001/XMLSchema-instance" {
400 return Err(self.error(ArxmlParserError::InvalidArxmlFileHeader));
401 }
402 self.fileversion = self.parse_file_version(schema)?;
403
404 Ok(())
405 } else {
406 Err(self.error(ArxmlParserError::InvalidArxmlFileHeader))
407 }
408 }
409
410 fn parse_file_version(&mut self, schema: &str) -> Result<AutosarVersion, AutosarDataError> {
412 let mut schema_parts = schema.split(' ');
413 let schema_base = schema_parts.next().unwrap_or("");
414 if schema_base != "http://autosar.org/schema/r4.0" {
415 return Err(self.error(ArxmlParserError::InvalidArxmlFileHeader));
416 }
417 let xsd_file_raw = schema_parts.next().unwrap_or("");
418 let xsd_file: String = if xsd_file_raw.starts_with("autosar") {
419 format!("AUTOSAR{}", xsd_file_raw.strip_prefix("autosar").unwrap())
420 } else {
421 xsd_file_raw.to_owned()
422 };
423 let version = if let Ok(autosar_version) = AutosarVersion::from_str(&xsd_file) {
424 autosar_version
425 } else if xsd_file == "AUTOSAR_4-3-1.xsd" {
426 self.optional_error(ArxmlParserError::InvalidAutosarVersion {
429 input_verstring: xsd_file.to_string(),
430 replacement: AutosarVersion::Autosar_00044,
431 })?;
432 AutosarVersion::Autosar_00044
433 } else if xsd_file == "AUTOSAR_4-4-0.xsd" {
434 self.optional_error(ArxmlParserError::InvalidAutosarVersion {
437 input_verstring: xsd_file.to_string(),
438 replacement: AutosarVersion::Autosar_00046,
439 })?;
440 AutosarVersion::Autosar_00046
441 } else if xsd_file == "AUTOSAR_4-5-0.xsd" {
442 self.optional_error(ArxmlParserError::InvalidAutosarVersion {
445 input_verstring: xsd_file.to_string(),
446 replacement: AutosarVersion::Autosar_00048,
447 })?;
448 AutosarVersion::Autosar_00048
449 } else {
450 self.optional_error(ArxmlParserError::UnknownAutosarVersion {
451 input_verstring: xsd_file.to_string(),
452 })?;
453 AutosarVersion::LATEST
454 };
455 Ok(version)
456 }
457
458 pub(crate) fn get_standalone(&self) -> Option<bool> {
460 self.standalone
461 }
462
463 fn parse_element(
465 &mut self,
466 raw_element: ElementRaw,
467 mut path: Cow<str>,
468 lexer: &mut ArxmlLexer,
469 ) -> Result<Element, AutosarDataError> {
470 let wrapped_element = raw_element.wrap();
471 let mut element = wrapped_element.0.write();
472
473 let mut elem_idx: Vec<usize> = Vec::new();
474 let mut short_name_found = false;
475
476 let mut stored_comment = None;
477 loop {
478 self.current_element = element.elemname;
480 let arxmlevent = self.next(lexer)?;
481 match arxmlevent {
482 ArxmlEvent::BeginElement(elem_text, attr_text) => {
483 if let Ok(name) = ElementName::from_bytes(elem_text) {
484 let (sub_elemtype, idx) = self.find_element_in_spec_checked(name, element.elemtype)?;
485 self.check_element_conflict(name, element.elemtype, &elem_idx, &idx)?;
486 elem_idx = idx;
487
488 if !element.content.is_empty() {
490 self.check_multiplicity(name, element.elemtype, &elem_idx, &element)?;
491 }
492
493 let new_element = ElementRaw {
495 parent: ElementOrModel::Element(wrapped_element.downgrade(), self.model.clone()),
496 elemname: name,
497 elemtype: sub_elemtype,
498 content: SmallVec::new(),
499 attributes: self.parse_attribute_text(sub_elemtype, attr_text)?,
500 file_membership: None,
501 comment: stored_comment,
502 };
503 let sub_element = self.parse_element(new_element, Cow::from(path.as_ref()), lexer)?;
504 stored_comment = None;
505 if name == ElementName::ShortName {
507 short_name_found = true;
508 let sub_element_inner = sub_element.0.read();
509 if let Some(ElementContent::CharacterData(CharacterData::String(name_string))) =
510 sub_element_inner.content.first()
511 {
512 let mut new_path = String::with_capacity(path.len() + name_string.len() + 1);
513 new_path.push_str(&path);
514 new_path.push('/');
515 new_path.push_str(name_string);
516 path = Cow::from(new_path.clone());
517 self.identifiables.push((new_path, wrapped_element.downgrade()));
518 } else {
519 return Err(self.error(ArxmlParserError::EmptyShortName {
526 element: element.elemname,
527 }));
528 }
529 }
530 element.content.push(ElementContent::Element(sub_element));
531 } else {
532 return Err(self.error(ArxmlParserError::InvalidBeginElement {
533 element: element.elemname,
534 invalid_element: String::from_utf8_lossy(elem_text).to_string(),
535 }));
536 }
537 }
538 ArxmlEvent::EndElement(elem_text) => {
539 if let Ok(name) = ElementName::from_bytes(elem_text) {
540 if name == element.elemname {
541 break;
542 }
543 return Err(self.error(ArxmlParserError::IncorrectEndElement {
544 element: element.elemname,
545 other_element: name,
546 }));
547 }
548 return Err(self.error(ArxmlParserError::InvalidEndElement {
549 parent_element: element.elemname,
550 invalid_element: String::from_utf8_lossy(elem_text).to_string(),
551 }));
552 }
553 ArxmlEvent::Characters(text_content) => {
554 if let Some(character_data_spec) = element.elemtype.chardata_spec() {
570 let value = self.parse_character_data(text_content, character_data_spec)?;
571 if element.elemtype.is_ref()
572 && let CharacterData::String(refpath) = &value
573 {
574 let base = element
575 .attribute_value(AttributeName::Base)
576 .and_then(|cdata| cdata.string_value());
577 self.references
578 .push((refpath.to_owned(), wrapped_element.downgrade(), base));
579 }
580 element.content.push(ElementContent::CharacterData(value));
581 } else {
582 self.optional_error(ArxmlParserError::CharacterContentForbidden {
583 element: element.elemname,
584 })?;
585 }
586 }
587 ArxmlEvent::ArxmlHeader(_) => self.optional_error(ArxmlParserError::UnexpectedXmlFileHeader {
588 element: element.elemname,
589 })?,
590 ArxmlEvent::EndOfFile => {
591 return Err(self.error(ArxmlParserError::UnexpectedEndOfFile {
592 element: element.elemname,
593 }));
594 }
595 ArxmlEvent::Comment(comment_bytes) => {
596 stored_comment = Some(String::from_utf8_lossy(comment_bytes).into());
597 }
598 }
599 }
600
601 if short_name_found {
602 } else if element.elemtype.is_named_in_version(self.fileversion) {
603 self.optional_error(ArxmlParserError::RequiredSubelementMissing {
604 element: element.elemname,
605 sub_element: ElementName::ShortName,
606 })?;
607 }
608
609 Ok(wrapped_element.clone())
610 }
611
612 fn find_element_in_spec_checked(
613 &mut self,
614 name: ElementName,
615 elemtype: ElementType,
616 ) -> Result<(ElementType, Vec<usize>), AutosarDataError> {
617 let (sub_elem_type, new_elem_indices) =
621 if let Some(result) = elemtype.find_sub_element(name, self.fileversion as u32) {
622 result
624 } else {
625 let (sub_elemtype, elem_idx) = elemtype.find_sub_element(name, u32::MAX).ok_or_else(|| {
627 self.error(ArxmlParserError::IncorrectBeginElement {
628 element: self.current_element,
629 sub_element: name,
630 })
631 })?;
632 let version_mask = elemtype.get_sub_element_version_mask(&elem_idx).unwrap();
635 self.check_version(
637 version_mask,
638 ArxmlParserError::ElementVersionError {
639 element: self.current_element,
640 sub_element: name,
641 version: self.fileversion,
642 },
643 )?;
644 (sub_elemtype, elem_idx)
645 };
646
647 Ok((sub_elem_type, new_elem_indices))
648 }
649
650 fn check_element_conflict(
651 &mut self,
652 name: ElementName,
653 elemtype: ElementType,
654 elem_indices: &[usize],
655 new_elem_indices: &Vec<usize>,
656 ) -> Result<(), AutosarDataError> {
657 if elem_indices.is_empty() || (elem_indices == new_elem_indices) {
658 } else {
661 let Some(group_type) = elemtype.find_common_group(elem_indices, new_elem_indices) else {
663 return Ok(());
664 };
665 let mode = group_type.content_mode();
666
667 match mode {
668 ContentMode::Sequence => {
669 }
674 ContentMode::Choice => {
675 self.optional_error(ArxmlParserError::ElementChoiceConflict {
676 element: self.current_element,
677 sub_element: name,
678 })?;
679 }
680 ContentMode::Characters => {
681 panic!("accepted a sub-element inside a character-only element");
683 }
684 _ => {}
685 }
686 }
687 Ok(())
688 }
689
690 fn check_multiplicity(
691 &mut self,
692 name: ElementName,
693 elemtype: ElementType,
694 elem_idx: &[usize],
695 element: &ElementRaw,
696 ) -> Result<(), AutosarDataError> {
697 if let Some(ContentMode::Sequence | ContentMode::Choice) = elemtype.get_sub_element_container_mode(elem_idx)
700 && let Some(multiplicity) = elemtype.get_sub_element_multiplicity(elem_idx)
701 {
702 if multiplicity != ElementMultiplicity::Any {
704 if element.content.iter().any(|ec| {
706 ec.unwrap_element()
707 .is_some_and(|subelem| subelem.element_name() == name)
708 }) {
709 self.optional_error(ArxmlParserError::TooManySubElements {
710 element: self.current_element,
711 sub_element: name,
712 })?;
713 }
714 }
715 }
716 Ok(())
717 }
718
719 fn parse_attribute_text(
720 &mut self,
721 elemtype: ElementType,
722 attributes_text: &[u8],
723 ) -> Result<SmallVec<[Attribute; 1]>, AutosarDataError> {
724 let mut attributes: SmallVec<[Attribute; 1]> = SmallVec::new();
725 let startpos = attributes_text
728 .iter()
729 .position(|c| !c.is_ascii_whitespace())
730 .unwrap_or(0);
731 let mut rem = &attributes_text[startpos..];
732 while let Some(mut equals_pos) = rem.iter().position(|c| *c == b'=') {
733 let attr_name_part = rem[..equals_pos].trim_ascii_end();
734 while let Some(c) = rem.get(equals_pos + 1)
736 && c.is_ascii_whitespace()
737 {
738 equals_pos += 1;
739 }
740 if rem.len() - equals_pos < 3 {
741 break;
743 }
744 let quote_char = rem[equals_pos + 1];
745 if quote_char != b'"' && quote_char != b'\'' {
746 break;
748 }
749 rem = &rem[equals_pos + 2..];
750 let Some(endquote_pos) = rem.iter().position(|c| c == "e_char) else {
751 break;
753 };
754 let attr_value_part = &rem[..endquote_pos];
755
756 if let Ok(attr_name) = AttributeName::from_bytes(attr_name_part) {
757 if let Some(AttributeSpec {
758 spec: ctype,
759 version: version_mask,
760 ..
761 }) = elemtype.find_attribute_spec(attr_name)
762 {
763 self.check_version(
764 version_mask,
765 ArxmlParserError::AttributeVersionError {
766 element: self.current_element,
767 attribute: attr_name,
768 version: self.fileversion,
769 },
770 )?;
771 let attr_value = self.parse_character_data(attr_value_part, ctype)?;
772 if let Some(pos) = attributes
773 .iter()
774 .position(|attr: &Attribute| attr.attrname == attr_name)
775 {
776 self.optional_error(ArxmlParserError::DuplicateAttributeError {
777 element: self.current_element,
778 attribute: attr_name,
779 })?;
780 attributes[pos].content = attr_value;
781 } else {
782 attributes.push(Attribute {
783 attrname: attr_name,
784 content: attr_value,
785 });
786 }
787 } else {
788 self.optional_error(ArxmlParserError::UnknownAttributeError {
789 element: self.current_element,
790 attribute: attr_name.to_string(),
791 })?;
792 }
793 } else {
794 self.optional_error(ArxmlParserError::UnknownAttributeError {
795 element: self.current_element,
796 attribute: String::from_utf8_lossy(attr_name_part).to_string(),
797 })?;
798 }
799
800 let mut nextattr_start = endquote_pos + 1;
802 while nextattr_start < rem.len() && rem[nextattr_start].is_ascii_whitespace() {
803 nextattr_start += 1;
804 }
805
806 if nextattr_start < rem.len() && nextattr_start == endquote_pos + 1 {
808 break;
810 }
811 rem = &rem[nextattr_start..];
812 }
813
814 if !rem.is_empty() && !rem.iter().all(|c| c.is_ascii_whitespace()) {
815 self.optional_error(ArxmlParserError::AttributeValueError {
816 element: self.current_element,
817 attribute_text: String::from_utf8_lossy(attributes_text).into_owned(),
818 })?;
819 }
820
821 for (name, _ctype, required) in elemtype.attribute_spec_iter() {
822 if required && !attributes.iter().any(|attr: &Attribute| attr.attrname == name) {
823 self.optional_error(ArxmlParserError::RequiredAttributeMissing {
824 element: self.current_element,
825 attribute: name,
826 })?;
827 }
828 }
829
830 Ok(attributes)
831 }
832
833 fn parse_character_data(
834 &mut self,
835 input: &[u8],
836 character_data_spec: &CharacterDataSpec,
837 ) -> Result<CharacterData, AutosarDataError> {
838 let trimmed_input = input.trim_ascii();
839 match character_data_spec {
840 CharacterDataSpec::Enum { items } => {
841 let value = EnumItem::from_bytes(trimmed_input).map_err(|_| {
842 self.error(ArxmlParserError::UnknownEnumItem {
843 value: String::from_utf8_lossy(trimmed_input).to_string(),
844 })
845 })?;
846 let (_, version) = items.iter().find(|(item, _)| *item == value).ok_or_else(|| {
847 self.error(ArxmlParserError::InvalidEnumItem {
848 element: self.current_element,
849 item: value,
850 })
851 })?;
852 self.check_version(
853 *version,
854 ArxmlParserError::EnumItemVersionError {
855 element: self.current_element,
856 enum_item: value,
857 version: self.fileversion,
858 },
859 )?;
860 Ok(CharacterData::Enum(value))
861 }
862 CharacterDataSpec::Pattern {
863 check_fn,
864 regex,
865 max_length,
866 } => {
867 let text = match std::str::from_utf8(trimmed_input) {
868 Ok(utf8string) => Cow::Borrowed(utf8string),
869 Err(err) => {
870 self.optional_error(ArxmlParserError::Utf8Error { source: err })?;
871 String::from_utf8_lossy(trimmed_input)
872 }
873 };
874 let unescaped_text = self.unescape_string(&text)?.into_owned();
876 if max_length.is_some() && unescaped_text.len() > max_length.unwrap() {
877 self.optional_error(ArxmlParserError::StringValueTooLong {
878 value: String::from_utf8_lossy(trimmed_input).to_string(), length: max_length.unwrap(),
880 })?;
881 }
882 if !check_fn(unescaped_text.as_bytes()) {
883 self.optional_error(ArxmlParserError::RegexMatchError {
884 value: String::from_utf8_lossy(trimmed_input).to_string(), regex: (*regex).to_string(),
886 })?;
887 }
888 Ok(CharacterData::String(unescaped_text))
889 }
890 CharacterDataSpec::String {
891 preserve_whitespace,
892 max_length,
893 } => {
894 let raw_text = if *preserve_whitespace { input } else { trimmed_input };
895 let text = match std::str::from_utf8(raw_text) {
896 Ok(utf8string) => Cow::from(utf8string),
897 Err(err) => {
898 self.optional_error(ArxmlParserError::Utf8Error { source: err })?;
899 String::from_utf8_lossy(raw_text)
900 }
901 };
902 let unescaped_text = self.unescape_string(&text)?.into_owned();
903 if max_length.is_some() && unescaped_text.len() > max_length.unwrap() {
904 self.optional_error(ArxmlParserError::StringValueTooLong {
905 value: String::from_utf8_lossy(trimmed_input).to_string(),
906 length: max_length.unwrap(),
907 })?;
908 }
909 Ok(CharacterData::String(unescaped_text))
910 }
911 CharacterDataSpec::UnsignedInteger => {
912 let strval = std::str::from_utf8(trimmed_input)
913 .map_err(|err| self.error(ArxmlParserError::Utf8Error { source: err }))?;
914 let value = match strval.parse::<u64>() {
915 Ok(parsed) => parsed,
916 Err(_) => {
917 self.optional_error(ArxmlParserError::InvalidNumber {
918 input: strval.to_owned(),
919 })?;
920 0
921 }
922 };
923 Ok(CharacterData::UnsignedInteger(value))
924 }
925 CharacterDataSpec::Float => {
926 let strval = std::str::from_utf8(trimmed_input)
927 .map_err(|err| self.error(ArxmlParserError::Utf8Error { source: err }))?;
928 let value = match strval.parse::<f64>() {
929 Ok(parsed) if !(parsed.is_infinite() || parsed.is_nan()) => parsed,
930 _ => {
931 self.optional_error(ArxmlParserError::InvalidNumber {
932 input: strval.to_owned(),
933 })?;
934 0.0
935 }
936 };
937 Ok(CharacterData::Float(value))
938 }
939 }
940 }
941
942 fn unescape_string<'b>(&mut self, input: &'b str) -> Result<Cow<'b, str>, AutosarDataError> {
943 if input.contains('&') {
944 let mut unescaped = String::with_capacity(input.len());
945 let mut rem = input;
946 while let Some(pos) = rem.find('&') {
947 unescaped.push_str(&rem[..pos]);
948 rem = &rem[pos..];
949 if rem.starts_with("<") {
950 unescaped.push('<');
951 rem = &rem[4..];
952 } else if rem.starts_with(">") {
953 unescaped.push('>');
954 rem = &rem[4..];
955 } else if rem.starts_with("&") {
956 unescaped.push('&');
957 rem = &rem[5..];
958 } else if rem.starts_with("'") {
959 unescaped.push('\'');
960 rem = &rem[6..];
961 } else if rem.starts_with(""") {
962 unescaped.push('"');
963 rem = &rem[6..];
964 } else if rem.starts_with("&#x") {
965 let mut valid = false;
967 if let Some(endpos) = rem.find(';') {
968 let hextxt = &rem[3..endpos];
969 if let Ok(hexval) = u32::from_str_radix(hextxt, 16)
970 && let Some(ch) = char::from_u32(hexval)
971 {
972 unescaped.push(ch);
973 rem = &rem[endpos + 1..];
974 valid = true;
975 }
976 }
977 if !valid {
978 self.optional_error(ArxmlParserError::InvalidXmlEntity {
979 input: input.to_owned(),
980 })?;
981 unescaped.push('&');
982 rem = &rem[1..];
983 }
984 } else if rem.starts_with("&#") {
985 let mut valid = false;
987 if let Some(endpos) = rem.find(';') {
988 let numtxt = &rem[2..endpos];
989 if let Ok(val) = u32::from_str(numtxt)
990 && let Some(ch) = char::from_u32(val)
991 {
992 unescaped.push(ch);
993 rem = &rem[endpos + 1..];
994 valid = true;
995 }
996 }
997 if !valid {
998 self.optional_error(ArxmlParserError::InvalidXmlEntity {
999 input: input.to_owned(),
1000 })?;
1001 unescaped.push('&');
1002 rem = &rem[1..];
1003 }
1004 } else {
1005 self.optional_error(ArxmlParserError::InvalidXmlEntity {
1006 input: input.to_owned(),
1007 })?;
1008 unescaped.push('&');
1009 rem = &rem[1..];
1010 }
1011 }
1012 unescaped.push_str(rem);
1013
1014 Ok(Cow::Owned(unescaped))
1015 } else {
1016 Ok(Cow::Borrowed(input))
1017 }
1018 }
1019
1020 pub(crate) fn get_fileversion(&self) -> AutosarVersion {
1021 self.fileversion
1022 }
1023
1024 fn verify_end_of_input(&mut self, lexer: &mut ArxmlLexer) -> Result<(), AutosarDataError> {
1025 let (_, next_event) = lexer.next()?;
1026 if let ArxmlEvent::EndOfFile = next_event {
1027 Ok(())
1028 } else {
1029 self.optional_error(ArxmlParserError::AdditionalDataError)?;
1030 Ok(())
1031 }
1032 }
1033
1034 pub(crate) fn check_arxml_header(&mut self) -> bool {
1036 let mut lexer = ArxmlLexer::new(self.buffer, self.filename.clone());
1037
1038 if let Ok(ArxmlEvent::ArxmlHeader(_)) = self.next(&mut lexer) {
1039 let mut arxmlevent = self.next(&mut lexer);
1041 while let Ok(ArxmlEvent::Comment(..)) = arxmlevent {
1042 arxmlevent = self.next(&mut lexer);
1043 }
1044 if let Ok(ArxmlEvent::BeginElement(elemname, attributes_text)) = arxmlevent
1045 && let Ok(ElementName::Autosar) = ElementName::from_bytes(elemname)
1046 && let Ok(attributes) = self.parse_attribute_text(ElementType::ROOT, attributes_text)
1047 && self.parse_file_header(&attributes).is_ok()
1048 {
1049 return true;
1051 }
1052 }
1053
1054 false
1055 }
1056}
1057
1058#[cfg(test)]
1059mod test {
1060 use crate::parser::*;
1061 use crate::*;
1062
1063 fn test_helper(buffer: &[u8], target_error: std::mem::Discriminant<ArxmlParserError>, optional: bool) {
1064 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), buffer, true);
1065 let result = parser.parse_arxml();
1066 if let Err(AutosarDataError::ParserError { source, .. }) = result {
1067 println!("Error result: {source:?}");
1068 assert_eq!(
1069 std::mem::discriminant(&source),
1070 target_error,
1071 "Did not get the expected parser error"
1072 );
1073 } else {
1074 panic!("Did not get any parser error when one was expected");
1075 }
1076
1077 if optional {
1078 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), buffer, false);
1079 let _result = parser.parse_arxml();
1080 if let Some(AutosarDataError::ParserError { source, .. }) = parser.warnings.first() {
1081 println!("Warnings result: {source:?}");
1082 assert_eq!(
1083 std::mem::discriminant(source),
1084 target_error,
1085 "Did not get the expected parser error"
1086 );
1087 } else {
1088 panic!("Did not get a parser warning");
1089 }
1090 }
1091 }
1092
1093 const INVALID_HEADER_1: &str = "BLA BLA bla";
1094 const INVALID_HEADER_2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1095 <something>"#;
1096 const INVALID_HEADER_3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1097 <AUTOSAR xsi:schemaLocation="nonsense" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">"#;
1098 const INVALID_HEADER_4: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1099 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00049.xsd" xmlns="http://other" xmlns:xsi="http://other">"#;
1100
1101 #[test]
1102 fn test_invalid_header() {
1103 test_helper(
1104 INVALID_HEADER_1.as_bytes(),
1105 std::mem::discriminant(&ArxmlParserError::InvalidArxmlFileHeader),
1106 false,
1107 );
1108 test_helper(
1109 INVALID_HEADER_2.as_bytes(),
1110 std::mem::discriminant(&ArxmlParserError::InvalidArxmlFileHeader),
1111 false,
1112 );
1113 test_helper(
1114 INVALID_HEADER_3.as_bytes(),
1115 std::mem::discriminant(&ArxmlParserError::InvalidArxmlFileHeader),
1116 false,
1117 );
1118 test_helper(
1119 INVALID_HEADER_4.as_bytes(),
1120 std::mem::discriminant(&ArxmlParserError::InvalidArxmlFileHeader),
1121 false,
1122 );
1123 }
1124
1125 const HDR_SINGLE_QUOTE: &str = r#"<?xml version='1.0' encoding='utf-8'?>
1126 <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'>
1127 </AUTOSAR>"#;
1128
1129 #[test]
1130 fn single_quotes_in_header() {
1131 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), HDR_SINGLE_QUOTE.as_bytes(), true);
1132 let result = parser.parse_arxml();
1133 assert!(result.is_ok());
1134 }
1135
1136 const SCHEMA_VERSION_LC: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1137 <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">
1138 </AUTOSAR>"#;
1139 const INVALID_VERSION_4_3_1: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1140 <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">
1141 </AUTOSAR>"#;
1142 const INVALID_VERSION_4_4_0: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1143 <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">
1144 </AUTOSAR>"#;
1145 const INVALID_VERSION_4_5_0: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1146 <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">
1147 </AUTOSAR>"#;
1148
1149 #[test]
1150 fn test_invalid_version() {
1151 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), SCHEMA_VERSION_LC.as_bytes(), true);
1152 let result = parser.parse_arxml();
1153 assert!(result.is_ok());
1154
1155 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidAutosarVersion {
1156 input_verstring: "".to_string(),
1157 replacement: AutosarVersion::Autosar_00044,
1158 });
1159 test_helper(INVALID_VERSION_4_3_1.as_bytes(), discriminant, true);
1160 test_helper(INVALID_VERSION_4_4_0.as_bytes(), discriminant, true);
1161 test_helper(INVALID_VERSION_4_5_0.as_bytes(), discriminant, true);
1162 }
1163
1164 const UNKNOWN_VERSION: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1165 <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">
1166 </AUTOSAR>"#;
1167
1168 #[test]
1169 fn test_unknown_version() {
1170 let discriminant = std::mem::discriminant(&ArxmlParserError::UnknownAutosarVersion {
1171 input_verstring: "".to_string(),
1172 });
1173 test_helper(UNKNOWN_VERSION.as_bytes(), discriminant, true);
1174 }
1175
1176 const UNEXPECTED_XML_FILE_HEADER: &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 <?xml version="1.0" encoding="utf-8"?>
1179 </AUTOSAR>"#;
1180
1181 #[test]
1182 fn test_unexpected_xml_file_header() {
1183 let discriminant = std::mem::discriminant(&ArxmlParserError::UnexpectedXmlFileHeader {
1184 element: ElementName::Autosar,
1185 });
1186 test_helper(UNEXPECTED_XML_FILE_HEADER.as_bytes(), discriminant, true);
1187 }
1188
1189 const INCORRECT_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 <ELEMENT>"#;
1192
1193 #[test]
1194 fn test_incorrect_begin_element() {
1195 let discriminant = std::mem::discriminant(&ArxmlParserError::IncorrectBeginElement {
1196 element: ElementName::Autosar,
1197 sub_element: ElementName::Autosar,
1198 });
1199 test_helper(INCORRECT_BEGIN_ELEMENT.as_bytes(), discriminant, false);
1200 }
1201
1202 const INVALID_BEGIN_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 <NOT_AN_AUTOSAR_ELEMENT>"#;
1205
1206 #[test]
1207 fn test_invalid_begin_element() {
1208 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidBeginElement {
1209 element: ElementName::Autosar,
1210 invalid_element: "".to_string(),
1211 });
1212 test_helper(INVALID_BEGIN_ELEMENT.as_bytes(), discriminant, false);
1213 }
1214
1215 const INCORRECT_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></AUTOSAR>"#;
1218
1219 #[test]
1220 fn test_incorrect_end_element() {
1221 let discriminant = std::mem::discriminant(&ArxmlParserError::IncorrectEndElement {
1222 element: ElementName::Autosar,
1223 other_element: ElementName::Autosar,
1224 });
1225 test_helper(INCORRECT_END_ELEMENT.as_bytes(), discriminant, false);
1226 }
1227
1228 const INVALID_END_ELEMENT: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1229 <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">
1230 <AR-PACKAGES></NOT_AN_AUTOSAR_ELEMENT>"#;
1231
1232 #[test]
1233 fn test_invalid_end_element() {
1234 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidEndElement {
1235 parent_element: ElementName::Autosar,
1236 invalid_element: "".to_string(),
1237 });
1238 test_helper(INVALID_END_ELEMENT.as_bytes(), discriminant, false);
1239 }
1240
1241 const ELEMENT_VERSION_ERROR: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1242 <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">
1243 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>TestPackage</SHORT-NAME><ELEMENTS><DIAGNOSTIC-ACCESS-PERMISSION>"#;
1244
1245 #[test]
1246 fn test_element_version_error() {
1247 let discriminant = std::mem::discriminant(&ArxmlParserError::ElementVersionError {
1248 element: ElementName::Autosar,
1249 sub_element: ElementName::Autosar,
1250 version: AutosarVersion::Autosar_00050,
1251 });
1252 test_helper(ELEMENT_VERSION_ERROR.as_bytes(), discriminant, false);
1253 }
1254
1255 const CHOICE_CONFLICT: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1256 <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">
1257 <AR-PACKAGES>
1258 <AR-PACKAGE>
1259 <SHORT-NAME>base</SHORT-NAME>
1260 <ELEMENTS>
1261 <DIAGNOSTIC-CONTRIBUTION-SET>
1262 <SHORT-NAME>dcs</SHORT-NAME>
1263 <COMMON-PROPERTIES>
1264 <DIAGNOSTIC-COMMON-PROPS-VARIANTS>
1265 <DIAGNOSTIC-COMMON-PROPS-CONDITIONAL>
1266 <DEBOUNCE-ALGORITHM-PROPSS>
1267 <DIAGNOSTIC-DEBOUNCE-ALGORITHM-PROPS>
1268 <SHORT-NAME>props</SHORT-NAME>
1269 <DEBOUNCE-ALGORITHM>
1270 <DIAG-EVENT-DEBOUNCE-COUNTER-BASED>
1271 <SHORT-NAME>abc</SHORT-NAME>
1272 </DIAG-EVENT-DEBOUNCE-COUNTER-BASED>
1273 <DIAG-EVENT-DEBOUNCE-TIME-BASED>
1274 <SHORT-NAME>def</SHORT-NAME>
1275 </DIAG-EVENT-DEBOUNCE-TIME-BASED>"#;
1276
1277 #[test]
1278 fn test_choice_conflict() {
1279 let discriminant = std::mem::discriminant(&ArxmlParserError::ElementChoiceConflict {
1280 element: ElementName::Autosar,
1281 sub_element: ElementName::Autosar,
1282 });
1283 test_helper(CHOICE_CONFLICT.as_bytes(), discriminant, true);
1284 }
1285
1286 const TOO_MANY_SUBELEMENTS: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1287 <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">
1288 <AR-PACKAGES>
1289 <AR-PACKAGE>
1290 <SHORT-NAME>base</SHORT-NAME>
1291 <SHORT-NAME>base</SHORT-NAME>"#;
1292
1293 #[test]
1294 fn test_too_many_sub_elements() {
1295 let discriminant = std::mem::discriminant(&ArxmlParserError::TooManySubElements {
1296 element: ElementName::Autosar,
1297 sub_element: ElementName::Autosar,
1298 });
1299 test_helper(TOO_MANY_SUBELEMENTS.as_bytes(), discriminant, true);
1300 }
1301
1302 const REQUIRED_SUB_ELEMENT_MISSING: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1303 <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">
1304 <AR-PACKAGES>
1305 <AR-PACKAGE></AR-PACKAGE>"#;
1306
1307 #[test]
1308 fn test_required_sub_element_missing() {
1309 let discriminant = std::mem::discriminant(&ArxmlParserError::RequiredSubelementMissing {
1310 element: ElementName::Autosar,
1311 sub_element: ElementName::Autosar,
1312 });
1313 test_helper(REQUIRED_SUB_ELEMENT_MISSING.as_bytes(), discriminant, false);
1314 }
1315
1316 const UNKNOWN_ATTRIBUTE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1317 <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">
1318 <AR-PACKAGES UnknownAttribute="value">
1319 </AR-PACKAGES></AUTOSAR>"#;
1320 const UNKNOWN_ATTRIBUTE_2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1321 <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">
1322 <AR-PACKAGES DEST="value">
1323 </AR-PACKAGES></AUTOSAR>"#;
1324
1325 #[test]
1326 fn test_unknown_attribute() {
1327 let discriminant = std::mem::discriminant(&ArxmlParserError::UnknownAttributeError {
1328 element: ElementName::Autosar,
1329 attribute: "".to_string(),
1330 });
1331 test_helper(UNKNOWN_ATTRIBUTE.as_bytes(), discriminant, true);
1332 let discriminant = std::mem::discriminant(&ArxmlParserError::UnknownAttributeError {
1333 element: ElementName::Autosar,
1334 attribute: "DEST".to_string(),
1335 });
1336 test_helper(UNKNOWN_ATTRIBUTE_2.as_bytes(), discriminant, true);
1337 }
1338
1339 const REQUIRED_ATTRIBUTE_MISSING: &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">
1341 </AUTOSAR>"#;
1342
1343 #[test]
1344 fn test_required_attribute_missing() {
1345 let discriminant = std::mem::discriminant(&ArxmlParserError::RequiredAttributeMissing {
1346 element: ElementName::Autosar,
1347 attribute: AttributeName::Accesskey,
1348 });
1349 test_helper(REQUIRED_ATTRIBUTE_MISSING.as_bytes(), discriminant, true);
1350 }
1351
1352 const CHARACTER_CONTENT_FORBIDDEN: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1353 <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">
1354 abcdef"#;
1355
1356 #[test]
1357 fn test_character_content_forbidden() {
1358 let discriminant = std::mem::discriminant(&ArxmlParserError::CharacterContentForbidden {
1359 element: ElementName::Autosar,
1360 });
1361 test_helper(CHARACTER_CONTENT_FORBIDDEN.as_bytes(), discriminant, false);
1362 }
1363
1364 const WRONG_ENUM_ITEM_VERSION: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1365 <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">
1366 <AR-PACKAGES>
1367 <AR-PACKAGE>
1368 <SHORT-NAME>base</SHORT-NAME>
1369 <ELEMENTS>
1370 <SYSTEM>
1371 <SHORT-NAME>System</SHORT-NAME>
1372 <FIBEX-ELEMENTS>
1373 <FIBEX-ELEMENT-REF-CONDITIONAL>
1374 <FIBEX-ELEMENT-REF DEST="SERVICE-INSTANCE-COLLECTION-SET">"#;
1375
1376 #[test]
1377 fn test_enum_item_version() {
1378 let discriminant = std::mem::discriminant(&ArxmlParserError::EnumItemVersionError {
1379 element: ElementName::Autosar,
1380 enum_item: EnumItem::Aa,
1381 version: AutosarVersion::Autosar_00050,
1382 });
1383 test_helper(WRONG_ENUM_ITEM_VERSION.as_bytes(), discriminant, false);
1384 }
1385
1386 const UNKNOWN_ENUM_ITEM: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1387 <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">
1388 <AR-PACKAGES>
1389 <AR-PACKAGE>
1390 <SHORT-NAME>base</SHORT-NAME>
1391 <ELEMENTS>
1392 <SYSTEM>
1393 <SHORT-NAME>System</SHORT-NAME>
1394 <FIBEX-ELEMENTS>
1395 <FIBEX-ELEMENT-REF-CONDITIONAL>
1396 <FIBEX-ELEMENT-REF DEST="invalid_value_for_the_test">"#;
1397
1398 #[test]
1399 fn test_unknown_enum_item() {
1400 let discriminant = std::mem::discriminant(&ArxmlParserError::UnknownEnumItem { value: "".to_string() });
1401 test_helper(UNKNOWN_ENUM_ITEM.as_bytes(), discriminant, false);
1402 }
1403
1404 const INVALID_ENUM_ITEM: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1405 <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">
1406 <AR-PACKAGES>
1407 <AR-PACKAGE>
1408 <SHORT-NAME>base</SHORT-NAME>
1409 <ELEMENTS>
1410 <SYSTEM>
1411 <SHORT-NAME>System</SHORT-NAME>
1412 <FIBEX-ELEMENTS>
1413 <FIBEX-ELEMENT-REF-CONDITIONAL>
1414 <FIBEX-ELEMENT-REF DEST="default">"#;
1415
1416 #[test]
1417 fn test_invalid_enum_item() {
1418 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidEnumItem {
1419 element: ElementName::Abs,
1420 item: EnumItem::Aa,
1421 });
1422 test_helper(INVALID_ENUM_ITEM.as_bytes(), discriminant, false);
1423 }
1424
1425 const STRING_VALUE_TOO_LONG: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1426 <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">
1427 <AR-PACKAGES><AR-PACKAGE>
1428 <SHORT-NAME>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</SHORT-NAME>
1429 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
1430
1431 #[test]
1432 fn test_string_value_too_long() {
1433 let discriminant = std::mem::discriminant(&ArxmlParserError::StringValueTooLong {
1434 value: "".to_string(),
1435 length: 1,
1436 });
1437 test_helper(STRING_VALUE_TOO_LONG.as_bytes(), discriminant, true);
1438 }
1439
1440 const REGEX_MATCH_ERROR: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1441 <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">
1442 <AR-PACKAGES><AR-PACKAGE>
1443 <SHORT-NAME>0a</SHORT-NAME>
1444 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
1445
1446 #[test]
1447 fn test_regex_match_error() {
1448 let discriminant = std::mem::discriminant(&ArxmlParserError::RegexMatchError {
1449 value: "".to_string(),
1450 regex: "".to_string(),
1451 });
1452 test_helper(REGEX_MATCH_ERROR.as_bytes(), discriminant, true);
1453 }
1454
1455 const UTF8_ERROR: &[u8] = b"<?xml version=\"1.0\" encoding=\"utf-8\"?>
1456 <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\">
1457 <AR-PACKAGES><AR-PACKAGE S=\"\xff\xff\">";
1458
1459 #[test]
1460 fn test_utf8_error() {
1461 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), UTF8_ERROR, true);
1462 let result = parser.parse_arxml();
1463 assert!(
1464 matches!(
1465 result,
1466 Err(AutosarDataError::ParserError {
1467 source: ArxmlParserError::Utf8Error { .. },
1468 ..
1469 })
1470 ),
1471 "Did not get the expected parser error"
1472 );
1473
1474 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), UTF8_ERROR, false);
1475 let _ = parser.parse_arxml();
1476 let warning = parser.warnings.first();
1477 assert!(
1478 matches!(
1479 warning,
1480 Some(AutosarDataError::ParserError {
1481 source: ArxmlParserError::Utf8Error { .. },
1482 ..
1483 })
1484 ),
1485 "Did not get the expected parser warning"
1486 );
1487 }
1488
1489 const UNEXPECTED_END_OF_FILE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1490 <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">"#;
1491
1492 #[test]
1493 fn test_unexpected_end_of_file() {
1494 let discriminant = std::mem::discriminant(&ArxmlParserError::UnexpectedEndOfFile {
1495 element: ElementName::Autosar,
1496 });
1497 test_helper(UNEXPECTED_END_OF_FILE.as_bytes(), discriminant, false);
1498 }
1499
1500 const INVALID_NUMBER: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1501 <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">
1502 <AR-PACKAGES><AR-PACKAGE>
1503 <SHORT-NAME>base</SHORT-NAME>
1504 <ELEMENTS><I-SIGNAL-I-PDU>
1505 <SHORT-NAME>Pdu</SHORT-NAME>
1506 <I-PDU-TIMING-SPECIFICATIONS><I-PDU-TIMING><TRANSMISSION-MODE-DECLARATION><TRANSMISSION-MODE-TRUE-TIMING><CYCLIC-TIMING>
1507 <TIME-PERIOD><TOLERANCE><ABSOLUTE-TOLERANCE><ABSOLUTE>not a number</ABSOLUTE>"#;
1508
1509 #[test]
1510 fn test_invalid_number() {
1511 let discriminant = std::mem::discriminant(&ArxmlParserError::InvalidNumber { input: "".to_string() });
1512 test_helper(INVALID_NUMBER.as_bytes(), discriminant, true);
1513 }
1514
1515 const EMPTY_SHORT_NAME: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1516 <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">
1517 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME></SHORT-NAME>
1518 <ELEMENTS><ECU-INSTANCE><SHORT-NAME>Ecu</SHORT-NAME></ECU-INSTANCE></ELEMENTS>
1519 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
1520
1521 const WHITESPACE_SHORT_NAME: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1524 <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">
1525 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME> </SHORT-NAME>
1526 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
1527
1528 const COMMENT_SHORT_NAME: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1530 <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">
1531 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME><!--the name goes here--></SHORT-NAME>
1532 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
1533
1534 const MISSING_SHORT_NAME: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1537 <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">
1538 <AR-PACKAGES><AR-PACKAGE>
1539 <ELEMENTS><ECU-INSTANCE><SHORT-NAME>Ecu</SHORT-NAME></ECU-INSTANCE></ELEMENTS>
1540 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#;
1541
1542 #[test]
1543 fn test_empty_short_name() {
1544 for buffer in [EMPTY_SHORT_NAME, WHITESPACE_SHORT_NAME, COMMENT_SHORT_NAME] {
1548 for strict in [true, false] {
1549 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), buffer.as_bytes(), strict);
1550 let result = parser.parse_arxml();
1551 println!("strict={strict}, result={result:?}");
1552 assert!(matches!(
1553 result,
1554 Err(AutosarDataError::ParserError {
1555 source: ArxmlParserError::EmptyShortName {
1556 element: ElementName::ArPackage
1557 },
1558 ..
1559 })
1560 ));
1561 assert!(parser.warnings.is_empty());
1563 }
1564 }
1565 }
1566
1567 #[test]
1568 fn test_missing_short_name_is_not_an_empty_short_name() {
1569 let model = AutosarModel::new();
1571 let (_file, warnings) = model
1572 .load_buffer(MISSING_SHORT_NAME.as_bytes(), "test.arxml", false)
1573 .unwrap();
1574 assert!(matches!(
1575 warnings.first(),
1576 Some(AutosarDataError::ParserError {
1577 source: ArxmlParserError::RequiredSubelementMissing {
1578 element: ElementName::ArPackage,
1579 sub_element: ElementName::ShortName,
1580 },
1581 ..
1582 })
1583 ));
1584 let el_ar_package = model
1585 .root_element()
1586 .get_sub_element(ElementName::ArPackages)
1587 .and_then(|e| e.get_sub_element(ElementName::ArPackage))
1588 .unwrap();
1589 assert!(!el_ar_package.is_identifiable());
1590
1591 let model = AutosarModel::new();
1593 assert!(matches!(
1594 model.load_buffer(MISSING_SHORT_NAME.as_bytes(), "test.arxml", true),
1595 Err(AutosarDataError::ParserError {
1596 source: ArxmlParserError::RequiredSubelementMissing { .. },
1597 ..
1598 })
1599 ));
1600 }
1601
1602 const ADDITIONAL_DATA: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1603 <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">
1604 </AUTOSAR>
1605 <extra>"#;
1606
1607 #[test]
1608 fn test_additional_data_error() {
1609 let discriminant = std::mem::discriminant(&ArxmlParserError::AdditionalDataError);
1610 test_helper(ADDITIONAL_DATA.as_bytes(), discriminant, true);
1611 }
1612
1613 #[test]
1614 fn unescape_entities() {
1615 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), &[], true);
1616 let result = parser
1617 .unescape_string("&&<FOO>""'  end")
1618 .unwrap();
1619 assert_eq!(&result, r#"&&<FOO>""' end"#);
1620 let result = parser.unescape_string("&&>FOO<""'end");
1621 assert!(result.is_err());
1622 let result = parser.unescape_string("&#abcde;");
1624 assert!(result.is_err());
1625 let result = parser.unescape_string("�");
1627 assert!(result.is_err());
1628 }
1629
1630 const PARSER_TEST_DATA: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1631 <!--comment-->
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 <!--comment-->
1634 <AR-PACKAGES>
1635 <AR-PACKAGE>
1636 <SHORT-NAME>base</SHORT-NAME>
1637 <ELEMENTS>
1638 <SYSTEM UUID="12345678" S="some string" T="2022-01-31T13:00:59Z">
1639 <SHORT-NAME>System</SHORT-NAME>
1640 <FIBEX-ELEMENTS>
1641 <FIBEX-ELEMENT-REF-CONDITIONAL>
1642 <FIBEX-ELEMENT-REF DEST="I-SIGNAL-I-PDU">/base/Pdu</FIBEX-ELEMENT-REF>
1643 </FIBEX-ELEMENT-REF-CONDITIONAL>
1644 </FIBEX-ELEMENTS>
1645 </SYSTEM>
1646 <I-SIGNAL-I-PDU>
1647 <SHORT-NAME>Pdu</SHORT-NAME>
1648 <I-PDU-TIMING-SPECIFICATIONS>
1649 <I-PDU-TIMING>
1650 <TRANSMISSION-MODE-DECLARATION>
1651 <TRANSMISSION-MODE-TRUE-TIMING>
1652 <CYCLIC-TIMING>
1653 <TIME-PERIOD>
1654 <TOLERANCE>
1655 <ABSOLUTE-TOLERANCE>
1656 <ABSOLUTE>1.0</ABSOLUTE>
1657 </ABSOLUTE-TOLERANCE>
1658 </TOLERANCE>
1659 </TIME-PERIOD>
1660 </CYCLIC-TIMING>
1661 </TRANSMISSION-MODE-TRUE-TIMING>
1662 </TRANSMISSION-MODE-DECLARATION>
1663 </I-PDU-TIMING>
1664 </I-PDU-TIMING-SPECIFICATIONS>
1665 </I-SIGNAL-I-PDU>
1666 </ELEMENTS>
1667 </AR-PACKAGE>
1668 </AR-PACKAGES>
1669 </AUTOSAR>
1670 "#;
1671
1672 #[test]
1673 fn test_basic_functionality() {
1674 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), PARSER_TEST_DATA.as_bytes(), true);
1675 let result = parser.parse_arxml();
1676 assert!(result.is_ok());
1677 }
1678
1679 const PARSER_TEST_RELATIVE_REFERENCE_BASE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1680 <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">
1681 <AR-PACKAGES>
1682 <AR-PACKAGE>
1683 <SHORT-NAME>base</SHORT-NAME>
1684 <REFERENCE-BASES>
1685 <REFERENCE-BASE>
1686 <SHORT-LABEL>default</SHORT-LABEL>
1687 <PACKAGE-REF DEST="AR-PACKAGE">/base</PACKAGE-REF>
1688 </REFERENCE-BASE>
1689 </REFERENCE-BASES>
1690 <ELEMENTS>
1691 <SYSTEM>
1692 <SHORT-NAME>System</SHORT-NAME>
1693 <FIBEX-ELEMENTS>
1694 <FIBEX-ELEMENT-REF-CONDITIONAL>
1695 <FIBEX-ELEMENT-REF DEST="I-SIGNAL-I-PDU" BASE="default">Pdu</FIBEX-ELEMENT-REF>
1696 </FIBEX-ELEMENT-REF-CONDITIONAL>
1697 </FIBEX-ELEMENTS>
1698 </SYSTEM>
1699 <I-SIGNAL-I-PDU>
1700 <SHORT-NAME>Pdu</SHORT-NAME>
1701 </I-SIGNAL-I-PDU>
1702 </ELEMENTS>
1703 </AR-PACKAGE>
1704 </AR-PACKAGES>
1705 </AUTOSAR>
1706 "#;
1707
1708 #[test]
1709 fn parse_reference_base_and_relative_reference() {
1710 let mut parser = ArxmlParser::new(
1711 PathBuf::from("test_buffer.arxml"),
1712 PARSER_TEST_RELATIVE_REFERENCE_BASE.as_bytes(),
1713 true,
1714 );
1715
1716 let result = parser.parse_arxml();
1717 assert!(result.is_ok());
1718
1719 assert_eq!(parser.references.len(), 2);
1722 assert!(parser.references.iter().any(|(refpath, _, _)| refpath == "/base"));
1723 assert!(parser.references.iter().any(|(refpath, _, _)| refpath == "Pdu"));
1724 }
1725
1726 const GT_IN_ATTRIBUTE_VALUE: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1727 <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">
1728 <AR-PACKAGES>
1729 <AR-PACKAGE S="a > b">
1730 <SHORT-NAME>x</SHORT-NAME>
1731 </AR-PACKAGE>
1732 </AR-PACKAGES>
1733 </AUTOSAR>
1734 "#;
1735
1736 #[test]
1737 fn gt_in_attribute_value() {
1738 let mut parser = ArxmlParser::new(
1740 PathBuf::from("test_buffer.arxml"),
1741 GT_IN_ATTRIBUTE_VALUE.as_bytes(),
1742 true,
1743 );
1744 let root = parser.parse_arxml().unwrap();
1745 let package = root
1746 .get_sub_element(ElementName::ArPackages)
1747 .and_then(|pkgs| pkgs.get_sub_element(ElementName::ArPackage))
1748 .unwrap();
1749 assert_eq!(
1750 package.attribute_value(AttributeName::S).unwrap(),
1751 CharacterData::String("a > b".to_string())
1752 );
1753 }
1754
1755 const EMPTY_CHARACTER_DATA: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1756 <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">
1757 <AR-PACKAGES>
1758 <AR-PACKAGE UUID="">
1759 <SHORT-NAME>x</SHORT-NAME>
1760 </AR-PACKAGE>
1761 </AR-PACKAGES>
1762 </AUTOSAR>
1763 "#;
1764
1765 #[test]
1766 fn test_empty_character_data() {
1767 let mut parser = ArxmlParser::new(
1768 PathBuf::from("test_buffer.arxml"),
1769 EMPTY_CHARACTER_DATA.as_bytes(),
1770 true,
1771 );
1772 let result = parser.parse_arxml();
1773 assert!(result.is_ok());
1774 }
1775
1776 const WHITESPACE_CHARACTER_DATA: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1777 <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">
1778 <AR-PACKAGES>
1779 <AR-PACKAGE UUID=" ">
1780 <SHORT-NAME>x</SHORT-NAME>
1781 </AR-PACKAGE>
1782 </AR-PACKAGES>
1783 </AUTOSAR>
1784 "#;
1785
1786 #[test]
1787 fn test_whitespace_character_data() {
1788 let mut parser = ArxmlParser::new(
1790 PathBuf::from("test_buffer.arxml"),
1791 WHITESPACE_CHARACTER_DATA.as_bytes(),
1792 true,
1793 );
1794 let result = parser.parse_arxml();
1795 assert!(result.is_ok());
1796 }
1797
1798 #[test]
1799 fn chardata_utf8_error() {
1800 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), b"", true);
1801 let mut parser_permissive = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), b"", false);
1802
1803 let pattern_spec = CharacterDataSpec::Pattern {
1805 check_fn: |_| true,
1806 regex: "",
1807 max_length: Some(2),
1808 };
1809 let result = parser.parse_character_data(b"ab", &pattern_spec);
1810 assert!(result.is_ok());
1811
1812 let result = parser.parse_character_data(&[0xff], &pattern_spec);
1814 assert!(result.is_err());
1815
1816 let result = parser_permissive.parse_character_data(&[0xff], &pattern_spec);
1818 assert!(result.is_ok());
1819
1820 let string_spec = CharacterDataSpec::String {
1822 max_length: Some(2),
1823 preserve_whitespace: false,
1824 };
1825 let result = parser.parse_character_data(b"ab", &string_spec);
1826 assert!(result.is_ok());
1827
1828 let result = parser.parse_character_data(b"abc", &string_spec);
1830 assert!(result.is_err());
1831
1832 let result = parser.parse_character_data(&[0xff], &string_spec);
1834 assert!(result.is_err());
1835
1836 let int_spec = CharacterDataSpec::UnsignedInteger;
1838 let result = parser.parse_character_data(b"123", &int_spec);
1839 assert!(result.is_ok());
1840
1841 let result = parser.parse_character_data(b"abc", &int_spec);
1843 assert!(result.is_err());
1844
1845 let result = parser.parse_character_data(&[0xff], &int_spec);
1847 assert!(result.is_err());
1848
1849 let float_spec = CharacterDataSpec::Float;
1851 let result = parser.parse_character_data(b"1.0", &float_spec);
1852 assert!(result.is_ok());
1853
1854 let result = parser.parse_character_data(b"abc", &float_spec);
1856 assert!(result.is_err());
1857
1858 let result = parser.parse_character_data(&[0xff], &float_spec);
1860 assert!(result.is_err());
1861 }
1862
1863 #[test]
1864 fn test_check_arxml_header() {
1865 let buffer = "abcde".as_bytes();
1866 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1867 assert!(!parser.check_arxml_header());
1868
1869 let buffer = r#"<?xml version="1.0" encoding="utf-8"?>abcde"#.as_bytes();
1870 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1871 assert!(!parser.check_arxml_header());
1872
1873 let buffer = r#"<?xml version="1.0" encoding="utf-8"?><abcde>"#.as_bytes();
1874 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1875 assert!(!parser.check_arxml_header());
1876
1877 let buffer = r#"<?xml version="1.0" encoding="utf-8"?><AUTOSAR abcde="abcde">"#.as_bytes();
1878 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1879 assert!(!parser.check_arxml_header());
1880
1881 let buffer = r#"<?xml version="1.0" encoding="utf-8"?>
1882<AUTOSAR>"#
1883 .as_bytes();
1884 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1885 assert!(!parser.check_arxml_header());
1886
1887 let buffer = r#"<?xml version="1.0" encoding="utf-8"?>
1888<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();
1889 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1890 assert!(!parser.check_arxml_header());
1891
1892 let buffer = r#"<?xml version="1.0" encoding="utf-8"?>
1893<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();
1894 let mut parser = ArxmlParser::new(PathBuf::from("test"), buffer, true);
1895 assert!(parser.check_arxml_header());
1896 }
1897
1898 #[test]
1899 fn parse_attribute_text() {
1900 let mut parser = ArxmlParser::new(PathBuf::from("test"), &[], true);
1901 let etype_arpackage = ElementType::ROOT
1903 .find_sub_element(ElementName::ArPackages, u32::MAX)
1904 .unwrap()
1905 .0
1906 .find_sub_element(ElementName::ArPackage, u32::MAX)
1907 .unwrap()
1908 .0;
1909
1910 let result = parser.parse_attribute_text(etype_arpackage, br#" "#);
1912 assert!(result.is_ok());
1913 let value = result.unwrap();
1914 assert!(value.is_empty());
1915
1916 let result = parser.parse_attribute_text(etype_arpackage, br#" abc "#);
1918 assert!(result.is_err());
1919
1920 let result = parser.parse_attribute_text(etype_arpackage, br#"abc="#);
1922 assert!(result.is_err());
1923
1924 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID="#);
1926 assert!(result.is_err());
1927
1928 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID=1234"#);
1930 assert!(result.is_err());
1931
1932 let value = parser
1934 .parse_attribute_text(etype_arpackage, br#"UUID="12345678""#)
1935 .unwrap();
1936 assert_eq!(value.len(), 1);
1937 assert_eq!(value[0].attrname, AttributeName::Uuid);
1938
1939 let value = parser
1941 .parse_attribute_text(etype_arpackage, br#"UUID="12345678" "#)
1942 .unwrap();
1943 assert_eq!(value.len(), 1);
1944
1945 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID="12345678" abc "#);
1947 assert!(result.is_err());
1948
1949 let value = parser
1951 .parse_attribute_text(etype_arpackage, br#"UUID='12345678'"#)
1952 .unwrap();
1953 assert_eq!(value.len(), 1);
1954
1955 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID='12345678"#);
1957 assert!(result.is_err());
1958
1959 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID='12345678""#);
1961 assert!(result.is_err());
1962
1963 let result = parser.parse_attribute_text(etype_arpackage, br#"UUID="12345678"T="2024-01-01""#);
1965 assert!(result.is_err());
1966
1967 let value = parser
1969 .parse_attribute_text(etype_arpackage, br#"UUID="12345678" T="2024-01-01""#)
1970 .unwrap();
1971 assert_eq!(value.len(), 2);
1972
1973 let value = parser
1975 .parse_attribute_text(etype_arpackage, br#"UUID="12345678" T="2024-01-01""#)
1976 .unwrap();
1977 assert_eq!(value.len(), 2);
1978
1979 let value = parser
1981 .parse_attribute_text(etype_arpackage, br#" UUID="12345678" T="2024-01-01""#)
1982 .unwrap();
1983 assert_eq!(value.len(), 2);
1984
1985 let value = parser
1987 .parse_attribute_text(etype_arpackage, br#" UUID = "12345678" T = "2024-01-01""#)
1988 .unwrap();
1989 assert_eq!(value.len(), 2);
1990 assert_eq!(value[0].attrname, AttributeName::Uuid);
1991 assert_eq!(value[1].attrname, AttributeName::T);
1992
1993 let result = parser.parse_attribute_text(etype_arpackage, br#" UUID="1" UUID="2""#);
1995 assert!(matches!(
1996 result,
1997 Err(AutosarDataError::ParserError {
1998 source: ArxmlParserError::DuplicateAttributeError { .. },
1999 ..
2000 })
2001 ));
2002 }
2003
2004 #[test]
2005 fn parse_attribute_text_non_strict() {
2006 let mut parser = ArxmlParser::new(PathBuf::from("test"), &[], false);
2007 let etype_arpackage = ElementType::ROOT
2008 .find_sub_element(ElementName::ArPackages, u32::MAX)
2009 .unwrap()
2010 .0
2011 .find_sub_element(ElementName::ArPackage, u32::MAX)
2012 .unwrap()
2013 .0;
2014
2015 let value = parser
2017 .parse_attribute_text(etype_arpackage, br#" UUID="1" UUID="2""#)
2018 .unwrap();
2019 assert_eq!(value.len(), 1);
2020 assert_eq!(value[0].attrname, AttributeName::Uuid);
2021 assert_eq!(value[0].content.to_string(), "2");
2022 assert!(matches!(
2023 parser.warnings.first(),
2024 Some(AutosarDataError::ParserError {
2025 source: ArxmlParserError::DuplicateAttributeError { .. },
2026 ..
2027 })
2028 ));
2029 }
2030
2031 #[test]
2032 fn parse_invalid_numbers_non_strict() {
2033 let mut parser = ArxmlParser::new(PathBuf::from("test"), &[], false);
2034
2035 let value = parser
2037 .parse_character_data(b"not a number", &CharacterDataSpec::UnsignedInteger)
2038 .unwrap();
2039 assert_eq!(value, CharacterData::UnsignedInteger(0));
2040
2041 let value = parser.parse_character_data(b"inf", &CharacterDataSpec::Float).unwrap();
2043 assert_eq!(value, CharacterData::Float(0.0));
2044
2045 assert_eq!(parser.warnings.len(), 2);
2046 for warning in &parser.warnings {
2047 assert!(matches!(
2048 warning,
2049 AutosarDataError::ParserError {
2050 source: ArxmlParserError::InvalidNumber { .. },
2051 ..
2052 }
2053 ));
2054 }
2055 }
2056
2057 #[test]
2058 fn unescape_entities_non_strict() {
2059 let mut parser = ArxmlParser::new(PathBuf::from("test_buffer.arxml"), &[], false);
2060
2061 let result = parser.unescape_string("a b").unwrap();
2063 assert_eq!(&result, "a b");
2064
2065 let result = parser.unescape_string("a&#xZZ;b").unwrap();
2067 assert_eq!(&result, "a&#xZZ;b");
2068 let result = parser.unescape_string("a�b").unwrap();
2069 assert_eq!(&result, "a�b");
2070
2071 let result = parser.unescape_string("a&#abcde;b").unwrap();
2073 assert_eq!(&result, "a&#abcde;b");
2074 let result = parser.unescape_string("a�b").unwrap();
2075 assert_eq!(&result, "a�b");
2076
2077 let result = parser.unescape_string("aЛ").unwrap();
2079 assert_eq!(&result, "aЛ");
2080 let result = parser.unescape_string("aAb").unwrap();
2081 assert_eq!(&result, "aAb");
2082
2083 assert_eq!(parser.warnings.len(), 7);
2084 for warning in &parser.warnings {
2085 assert!(matches!(
2086 warning,
2087 AutosarDataError::ParserError {
2088 source: ArxmlParserError::InvalidXmlEntity { .. },
2089 ..
2090 }
2091 ));
2092 }
2093 }
2094}