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