autosar_data_specification/
lib.rs

1//! Specification of the Autosar arxml file format in the form of rust data structures
2//!
3//! This crate exists to support the autosar-data crate.
4//!
5//! The Autosar data model is originally specified as .xsd files - one for each version of the standard.
6//! All these separate xsd files were parsed into data structures and combined; this crate contains the
7//! combined specification data of all 21 Autosar 4 standard revisions.
8//!
9//! ## Supported standards:
10//!
11//! | xsd filename        | description               |
12//! |---------------------|---------------------------|
13//! | `AUTOSAR_4-0-1.xsd` | AUTOSAR 4.0.1             |
14//! | `AUTOSAR_4-0-2.xsd` | AUTOSAR 4.0.2             |
15//! | `AUTOSAR_4-0-3.xsd` | AUTOSAR 4.0.3             |
16//! | `AUTOSAR_4-1-1.xsd` | AUTOSAR 4.1.1             |
17//! | `AUTOSAR_4-1-2.xsd` | AUTOSAR 4.1.2             |
18//! | `AUTOSAR_4-1-3.xsd` | AUTOSAR 4.1.3             |
19//! | `AUTOSAR_4-2-1.xsd` | AUTOSAR 4.2.1             |
20//! | `AUTOSAR_4-2-2.xsd` | AUTOSAR 4.2.2             |
21//! | `AUTOSAR_4-3-0.xsd` | AUTOSAR 4.3.0             |
22//! | `AUTOSAR_00042.xsd` | AUTOSAR Adaptive 17-03    |
23//! | `AUTOSAR_00043.xsd` | AUTOSAR Adaptive 17-10    |
24//! | `AUTOSAR_00044.xsd` | AUTOSAR Classic 4.3.1     |
25//! | `AUTOSAR_00045.xsd` | AUTOSAR Adaptive 18-03    |
26//! | `AUTOSAR_00046.xsd` | AUTOSAR Classic 4.4.0 / Adaptive 18-10 |
27//! | `AUTOSAR_00047.xsd` | AUTOSAR Adaptive 19-03    |
28//! | `AUTOSAR_00048.xsd` | AUTOSAR 4.5.0             |
29//! | `AUTOSAR_00049.xsd` | AUTOSAR R20-11            |
30//! | `AUTOSAR_00050.xsd` | AUTOSAR R21-11            |
31//! | `AUTOSAR_00051.xsd` | AUTOSAR R22-11            |
32//! | `AUTOSAR_00052.xsd` | AUTOSAR R23-11            |
33//! | `AUTOSAR_00053.xsd` | AUTOSAR R24-11            |
34//!
35//! ## Using the crate
36//!
37//! The main datatype is the [`ElementType`]. The type of the <AUTOSAR> element at the root of every arxml file is
38//! available as `ElementType::ROOT`.
39//!
40//! ## Crate features
41//!
42//! * **docstrings** - Enables the function `ElementType::docstring`, which allows you to retrieve element documentation.
43//!   This feature increases the size of the compiled code, because all docstrings are compiled in. It is disabled by default.
44//!
45//! ## Note
46//!
47//! It is not possible to directly convert between [`ElementName`]s and [`ElementType`]s, since this is an n:m mapping.
48//! If the content of two differently named elements is structurally identical, then they have the same [`ElementType`];
49//! on the other side there are several elements that have different content depending in the context in which they appear.
50//!
51//! ## Example
52//!
53//! ```
54//! # use autosar_data_specification::*;
55//! # use std::str::FromStr;
56//! let root_type = ElementType::ROOT;
57//!
58//! // parsing an element
59//! let element_name_text = "AR-PACKAGES";
60//! let element_name = ElementName::from_str(element_name_text).unwrap();
61//! assert_eq!(element_name, ElementName::ArPackages);
62//!
63//! let version_mask = AutosarVersion::Autosar_4_3_0 as u32;
64//! if let Some((element_type, index_list)) = root_type.find_sub_element(
65//!     element_name,
66//!     version_mask
67//! ) {
68//!     // parsing an attribute
69//!     let attribute_name = AttributeName::from_str("UUID").unwrap();
70//!     if let Some(attribute_spec) = element_type.find_attribute_spec(attribute_name) {
71//!         // ...
72//!     }
73//!
74//!     // ...
75//! }
76//! ```
77#![no_std]
78
79#[macro_use]
80extern crate alloc;
81use alloc::vec::Vec;
82use core::ops::BitXor;
83
84mod attributename;
85mod autosarversion;
86mod elementname;
87mod enumitem;
88mod regex;
89mod specification;
90
91pub use attributename::{AttributeName, ParseAttributeNameError};
92pub use autosarversion::{AutosarVersion, ParseAutosarVersionError};
93pub use elementname::{ElementName, ParseElementNameError};
94pub use enumitem::{EnumItem, ParseEnumItemError};
95use specification::{
96    ATTRIBUTES, AUTOSAR_ELEMENT, CHARACTER_DATA, DATATYPES, ELEMENTS, REF_ITEMS, REFERENCE_TYPE_IDX, SUBELEMENTS,
97    VERSION_INFO,
98};
99
100/// `ElementMultiplicity` specifies how often a single child element may occur within its parent
101#[derive(Debug, Copy, Clone, Eq, PartialEq)]
102pub enum ElementMultiplicity {
103    ZeroOrOne,
104    One,
105    Any,
106}
107
108/// `StdRestrict` is used to indicate if an element is restricted to either Classic or Adaptive
109#[derive(Debug, Copy, Clone, Eq, PartialEq)]
110pub enum StdRestrict {
111    NotRestricted,
112    ClassicPlatform,
113    AdaptivePlatform,
114}
115
116/// The `ContentMode` specifies what content may occur inside an element
117#[derive(Debug, Copy, Clone, Eq, PartialEq)]
118pub enum ContentMode {
119    /// `Sequence`: an ordered sequence of elements
120    Sequence,
121    /// `Choice`: a single element must be chosen from multiple options.
122    /// If the multiplicity of the chosen element is `Any` then it may repeat so there might still be more than one sub element
123    Choice,
124    /// `Bag`: From a list of choices, choose a sub element any number of times.
125    /// In this content mode all allowed sub elements may occur any number of times and in any order
126    Bag,
127    /// `Characters`: no sub elements are permitted, there can only be character content
128    Characters,
129    /// `Mixed`: both characters content and sub elements are allowed, in any order. It's basically like HTML
130    Mixed,
131}
132
133/// Specifies the data type and restrictions of the character data in an element or attribute
134pub enum CharacterDataSpec {
135    /// The character data is an enum value; valid values are given in items and the character data must match one of these
136    Enum {
137        items: &'static [(EnumItem, u32)],
138    },
139    /// The character data is restricted to match a regular expression, which is given in text form in the field `regex`.
140    Pattern {
141        /// The `check_fn` is a function that validates input according to the regex.
142        check_fn: fn(&[u8]) -> bool,
143        // Regular expression as a string; it is only informational. Checking is performed by `check_fn`
144        regex: &'static str,
145        /// If a `max_length` is given, then it restricts the length (in bytes).
146        max_length: Option<usize>,
147    },
148    /// An arbitrary string; if preserve whitepace is set, then whitespace should be preserved during parsing (see the XML standard)
149    String {
150        preserve_whitespace: bool,
151        max_length: Option<usize>,
152    },
153    UnsignedInteger,
154    Float,
155}
156
157/// specification of an attribute
158pub struct AttributeSpec {
159    /// data type of the attribute content
160    pub spec: &'static CharacterDataSpec,
161    /// is the attribute required to be present in it's containing element
162    pub required: bool,
163    /// in which autosar version(s) is this attribute valid. This field is a bitmask.
164    pub version: u32,
165}
166
167/// `ElementType` is an abstraction over element types in the specification.
168///
169/// It provides no public fields, but it has methods to get all the info needed to parse an arxml element.
170#[derive(Eq, PartialEq, Clone, Copy, Hash)]
171pub struct ElementType {
172    /// index into the `ELEMENTS` array
173    def: u16,
174    /// index into the `DATATYPES` array
175    typ: u16,
176}
177
178/// `GroupType` is an abstraction over groups of elements in the specification.
179///
180/// It provides no public fields.
181#[derive(Debug, Clone, Copy)]
182pub struct GroupType(u16);
183
184#[derive(Debug)]
185enum SubElement {
186    Element(u16),
187    Group(u16),
188}
189
190struct ElementDefinition {
191    name: ElementName,
192    elemtype: u16,
193    multiplicity: ElementMultiplicity,
194    ordered: bool,
195    splittable: u32,
196    restrict_std: StdRestrict,
197    #[cfg(feature = "docstrings")]
198    docstring: Option<u16>,
199}
200
201struct ElementSpec {
202    sub_elements: (u16, u16),
203    sub_element_ver: u16,
204    attributes: (u16, u16),
205    attributes_ver: u16,
206    character_data: Option<u16>,
207    mode: ContentMode,
208    ref_info: (u16, u16),
209}
210
211impl AutosarVersion {
212    #[must_use]
213    pub fn compatible(&self, version_mask: u32) -> bool {
214        version_mask & *self as u32 != 0
215    }
216}
217
218impl ElementType {
219    #[must_use]
220    const fn new(def: u16) -> Self {
221        let typ = ELEMENTS[def as usize].elemtype;
222        Self { def, typ }
223    }
224
225    fn get_sub_elements(etype: u16) -> &'static [SubElement] {
226        let (idx_start, idx_end) = ElementType::get_sub_element_idx(etype);
227        &SUBELEMENTS[idx_start..idx_end]
228    }
229
230    const fn get_sub_element_idx(etype: u16) -> (usize, usize) {
231        let (start, end) = DATATYPES[etype as usize].sub_elements;
232        (start as usize, end as usize)
233    }
234
235    const fn get_sub_element_ver(etype: u16) -> usize {
236        DATATYPES[etype as usize].sub_element_ver as usize
237    }
238
239    const fn get_attributes_idx(etype: u16) -> (usize, usize) {
240        let (start, end) = DATATYPES[etype as usize].attributes;
241        (start as usize, end as usize)
242    }
243
244    const fn get_attributes_ver(etype: u16) -> usize {
245        DATATYPES[etype as usize].attributes_ver as usize
246    }
247
248    /// get the spec of a sub element from the index list
249    fn get_sub_element_spec<'a>(self, element_indices: &[usize]) -> Option<(&'a SubElement, u32)> {
250        if element_indices.is_empty() {
251            return None;
252        }
253
254        let spec = ElementType::get_sub_elements(self.typ);
255        let ver_list_start = ElementType::get_sub_element_ver(self.typ);
256        let mut current_spec = spec;
257        let mut current_ver_list_start = ver_list_start;
258        // go through the hierarchy of groups: only the final index in element_indices can refer to a SubElement::Element
259        for idx in 0..(element_indices.len() - 1) {
260            match &current_spec[element_indices[idx]] {
261                SubElement::Element { .. } => {
262                    // elements are not allowed here
263                    return None;
264                }
265                SubElement::Group(groupid) => {
266                    current_spec = ElementType::get_sub_elements(*groupid);
267                    current_ver_list_start = ElementType::get_sub_element_ver(*groupid);
268                }
269            }
270        }
271
272        let last_idx = *element_indices.last().unwrap();
273        Some((&current_spec[last_idx], VERSION_INFO[current_ver_list_start + last_idx]))
274    }
275
276    /// get the version mask of a sub element
277    #[must_use]
278    pub fn get_sub_element_version_mask(&self, element_indices: &[usize]) -> Option<u32> {
279        match self.get_sub_element_spec(element_indices) {
280            Some((_, version_mask)) => Some(version_mask),
281            _ => None,
282        }
283    }
284
285    /// get the multiplicity of a sub element within the current `ElementType`
286    ///
287    /// The sub element is identified by an indx list, as returned by `find_sub_element()`
288    #[must_use]
289    pub fn get_sub_element_multiplicity(&self, element_indices: &[usize]) -> Option<ElementMultiplicity> {
290        match self.get_sub_element_spec(element_indices) {
291            Some((SubElement::Element(definiton_id), _)) => Some(ELEMENTS[*definiton_id as usize].multiplicity),
292            _ => None,
293        }
294    }
295
296    /// get the `ContentMode` of the container of a sub element of the current `ElementType`
297    ///
298    /// The sub element is identified by an index list, as returned by `find_sub_element()`
299    #[must_use]
300    pub fn get_sub_element_container_mode(&self, element_indices: &[usize]) -> ContentMode {
301        if element_indices.len() < 2 {
302            // length == 1: this element is a direct sub element, without any groups;
303            DATATYPES[self.typ as usize].mode
304        } else {
305            let len = element_indices.len() - 1;
306            if let Some((SubElement::Group(groupid), _)) = self.get_sub_element_spec(&element_indices[..len]) {
307                DATATYPES[*groupid as usize].mode
308            } else {
309                unreachable!("impossible: element container is not a group");
310            }
311        }
312    }
313
314    /// find a sub element in the specification of the current `ElementType`
315    ///
316    /// Note: Version here is NOT an `AutosarVersion`, it is a u32. it is a bitmask which can contain multiple `AutosarVersions`, or any version by using `u32::MAX`
317    ///
318    /// In almost all cases this is simple: there is a flat list of sub elements that either contains the `target_name` or not.
319    /// The result in those simple cases is a vec with one entry which is the index of the element in the list.
320    /// There are a handfull of complicated situations though, where the list of sub elements contains groups of
321    /// elements that have a different `ContentMode` than the other elements.
322    ///
323    /// For example:
324    /// ```text
325    ///     PRM-CHAR (Sequence)
326    ///      -> Element: COND
327    ///      -> Group (Choice)
328    ///         -> Group (Sequence)
329    ///             -> Group (Choice)
330    ///                 -> Group (Sequence)
331    ///                     -> Element: ABS
332    ///                     -> Element: TOL
333    ///                 -> Group (Sequence)
334    ///                     -> Element: MIN
335    ///                     -> Element: TYP
336    ///                     -> Element: MAX
337    ///             -> Element: PRM-UNIT
338    ///         -> Element: TEXT
339    ///      -> Element: REMARK
340    /// ```
341    /// When searching for TOL in PRM-CHAR, the result should be Some(vec![1, 0, 0, 0, 1])!
342    #[must_use]
343    pub fn find_sub_element(&self, target_name: ElementName, version: u32) -> Option<(ElementType, Vec<usize>)> {
344        ElementType::find_sub_element_internal(self.typ, target_name, version)
345    }
346
347    fn find_sub_element_internal(
348        etype: u16,
349        target_name: ElementName,
350        version: u32,
351    ) -> Option<(ElementType, Vec<usize>)> {
352        let spec = ElementType::get_sub_elements(etype);
353        for (cur_pos, sub_element) in spec.iter().enumerate() {
354            match sub_element {
355                SubElement::Element(definiton_id) => {
356                    let name = ELEMENTS[*definiton_id as usize].name;
357                    let ver_info_start = ElementType::get_sub_element_ver(etype);
358                    let version_mask = VERSION_INFO[ver_info_start + cur_pos];
359                    if (name == target_name) && (version & version_mask != 0) {
360                        return Some((ElementType::new(*definiton_id), vec![cur_pos]));
361                    }
362                }
363                SubElement::Group(groupid) => {
364                    if let Some((elemtype, mut indices)) =
365                        ElementType::find_sub_element_internal(*groupid, target_name, version)
366                    {
367                        indices.insert(0, cur_pos);
368                        return Some((elemtype, indices));
369                    }
370                }
371            }
372        }
373        None
374    }
375
376    /// find the commmon group of two subelements of the current `ElementType`
377    ///
378    /// The subelements are identified by their index lists, returned by `find_sub_element`().
379    ///
380    /// In simple cases without sub-groups of elements, the "common group" is simply the element group of the current `ElementType`.
381    #[must_use]
382    pub fn find_common_group(&self, element_indices: &[usize], element_indices2: &[usize]) -> GroupType {
383        let mut result = self.typ;
384        let mut prefix_len = 0;
385        while element_indices.len() > prefix_len
386            && element_indices2.len() > prefix_len
387            && element_indices[prefix_len] == element_indices2[prefix_len]
388        {
389            let sub_elem = &ElementType::get_sub_elements(result)[element_indices[prefix_len]];
390            match sub_elem {
391                SubElement::Element(_) => return GroupType(result),
392                SubElement::Group(groupid) => {
393                    result = *groupid;
394                }
395            }
396            prefix_len += 1;
397        }
398
399        GroupType(result)
400    }
401
402    /// are elements of this `ElementType` named in any Autosar version
403    #[must_use]
404    pub fn is_named(&self) -> bool {
405        self.short_name_version_mask().is_some()
406    }
407
408    pub(crate) fn short_name_version_mask(self) -> Option<u32> {
409        let sub_elements = ElementType::get_sub_elements(self.typ);
410        if !sub_elements.is_empty()
411            && let SubElement::Element(idx) = sub_elements[0]
412            && ELEMENTS[idx as usize].name == ElementName::ShortName
413        {
414            let ver_idx = ElementType::get_sub_element_ver(self.typ);
415            return Some(VERSION_INFO[ver_idx]);
416        }
417        None
418    }
419
420    /// are elements of this elementType named in the given Autosar version
421    ///
422    /// Named elements must have a SHORT-NAME sub element. For some elements this
423    /// depends on the Autosar version.
424    ///
425    /// One example of this is END-2-END-METHOD-PROTECTION-PROPS, which was first
426    /// defined in `Autosar_00048`, but only has a name in `Autosar_00050`.
427    #[must_use]
428    pub fn is_named_in_version(&self, version: AutosarVersion) -> bool {
429        self.short_name_version_mask()
430            .is_some_and(|ver_mask| version.compatible(ver_mask))
431    }
432
433    /// is the `ElementType` a reference
434    #[must_use]
435    pub fn is_ref(&self) -> bool {
436        if let Some(idx) = DATATYPES[self.typ as usize].character_data {
437            idx == REFERENCE_TYPE_IDX
438        } else {
439            false
440        }
441    }
442
443    /// get the content mode for this `ElementType`
444    #[must_use]
445    pub const fn content_mode(&self) -> ContentMode {
446        DATATYPES[self.typ as usize].mode
447    }
448
449    /// get the character data spec for this `ElementType`
450    #[must_use]
451    pub const fn chardata_spec(&self) -> Option<&'static CharacterDataSpec> {
452        if let Some(chardata_id) = DATATYPES[self.typ as usize].character_data {
453            Some(&CHARACTER_DATA[chardata_id as usize])
454        } else {
455            None
456        }
457    }
458
459    /// find the spec for a single attribute by name
460    #[must_use]
461    pub fn find_attribute_spec(&self, attrname: AttributeName) -> Option<AttributeSpec> {
462        let (idx_start, idx_end) = ElementType::get_attributes_idx(self.typ);
463        let attributes = &ATTRIBUTES[idx_start..idx_end];
464        if let Some((find_pos, (_, chardata_id, required))) =
465            attributes.iter().enumerate().find(|(_, (name, ..))| *name == attrname)
466        {
467            let idx_ver_start = ElementType::get_attributes_ver(self.typ);
468            let version = VERSION_INFO[idx_ver_start + find_pos];
469            Some(AttributeSpec {
470                spec: &CHARACTER_DATA[*chardata_id as usize],
471                required: *required,
472                version,
473            })
474        } else {
475            None
476        }
477    }
478
479    /// create an iterator over all attribute definitions in the current `ElementType`
480    #[must_use]
481    pub const fn attribute_spec_iter(&self) -> AttrDefinitionsIter {
482        AttrDefinitionsIter {
483            type_id: self.typ,
484            pos: 0,
485        }
486    }
487
488    /// create an iterator over all sub elements of the current `ElementType`
489    #[must_use]
490    pub fn sub_element_spec_iter(&self) -> SubelemDefinitionsIter {
491        SubelemDefinitionsIter {
492            type_id_stack: vec![self.typ],
493            indices: vec![0],
494        }
495    }
496
497    /// Is this `ElementType` ordered
498    ///
499    /// It this is true, then the position of the sub elements of this element is semantically meaningful
500    /// and they may not be sorted / re-ordered without changing the meaning of the file.
501    ///
502    /// An example of this is ARGUMENTS in BSW-MODULE-ENTRY. ARGUMENTS is ordered, because each of its
503    /// SW-SERVICE-ARG sub elements represents a function argument
504    #[must_use]
505    pub const fn is_ordered(&self) -> bool {
506        ELEMENTS[self.def as usize].ordered
507    }
508
509    /// Is this `ElementType` splittable
510    ///
511    /// This function returns a bitfield that indicates in which versions (if any) the `ElementType` is marked as splittable.
512    /// A splittable element may be split across multiple arxml files
513    #[must_use]
514    pub const fn splittable(&self) -> u32 {
515        ELEMENTS[self.def as usize].splittable
516    }
517
518    /// Is the current `ElementType` splittable in the given version
519    ///
520    /// A splittable element may be split across multiple arxml files
521    #[must_use]
522    pub const fn splittable_in(&self, version: AutosarVersion) -> bool {
523        (ELEMENTS[self.def as usize].splittable & (version as u32)) != 0
524    }
525
526    /// Is this `ElementType` restricted to a particular edition of the Autosar standard
527    ///
528    /// Returns an [`StdRestrict`] enum, whose values are `ClassicPlatform`, `AdaptivePlatform`, `NotRestricted`
529    #[must_use]
530    pub const fn std_restriction(&self) -> StdRestrict {
531        ELEMENTS[self.def as usize].restrict_std
532    }
533
534    /// find the correct `EnumItem` to use in the DEST attribute when referring from this element to the other element
535    ///
536    /// Returns `Some(enum_item)` if the reference is possible, and None otherwise.
537    ///
538    /// Example:
539    ///
540    /// When referring to a `<CAN-TP-CONNECTION><IDENT><SHORT-NAME>foo...`
541    /// the referrring `<PHYSICAL-REQUEST-REF [...]>` must set DEST="TP-CONNECTION-IDENT"
542    #[must_use]
543    pub fn reference_dest_value(&self, other: &ElementType) -> Option<EnumItem> {
544        // this element must be a reference, and the other element must be identifiable, otherwise it is not a valid target
545        if self.is_ref() && other.is_named() {
546            let dest_spec = self.find_attribute_spec(AttributeName::Dest)?.spec;
547            if let CharacterDataSpec::Enum { items } = dest_spec {
548                let (start, end) = DATATYPES[other.typ as usize].ref_info;
549                let ref_by = &REF_ITEMS[start as usize..end as usize];
550                for ref_target_value in ref_by {
551                    for (enumitem, _) in *items {
552                        if ref_target_value == enumitem {
553                            return Some(*ref_target_value);
554                        }
555                    }
556                }
557            }
558        }
559        None
560    }
561
562    /// verify that the given `dest_value` is a valid enum item that can be used to refer to this element type
563    #[must_use]
564    pub fn verify_reference_dest(&self, dest_value: EnumItem) -> bool {
565        let (start, end) = DATATYPES[self.typ as usize].ref_info;
566        let values = &REF_ITEMS[start as usize..end as usize];
567        values.contains(&dest_value)
568    }
569
570    #[cfg(feature = "docstrings")]
571    #[must_use]
572    pub const fn docstring(&self) -> &str {
573        if let Some(docstring_id) = ELEMENTS[self.def as usize].docstring {
574            specification::ELEMENT_DOCSTRINGS[docstring_id as usize]
575        } else {
576            ""
577        }
578    }
579
580    /// `ElementType::ROOT` is the root `ElementType` of the Autosar arxml document: this is the `ElementType` of the AUTOSAR element
581    pub const ROOT: Self = ElementType::new(AUTOSAR_ELEMENT);
582}
583
584/// custom implementation of Debug for ElementType - make the output more compact, since the long form is not very useful
585impl core::fmt::Debug for ElementType {
586    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
587        write!(f, "ElementType({}, {})", self.def, self.typ)
588    }
589}
590
591impl GroupType {
592    /// get the content mode for this `GroupType`
593    #[must_use]
594    pub const fn content_mode(&self) -> ContentMode {
595        DATATYPES[self.0 as usize].mode
596    }
597}
598
599/// Iterator for attribute definitions
600pub struct AttrDefinitionsIter {
601    type_id: u16,
602    pos: usize,
603}
604
605impl Iterator for AttrDefinitionsIter {
606    type Item = (AttributeName, &'static CharacterDataSpec, bool);
607
608    fn next(&mut self) -> Option<Self::Item> {
609        let (idx_start, idx_end) = ElementType::get_attributes_idx(self.type_id);
610        let cur_pos = self.pos;
611        self.pos += 1;
612        if idx_start + cur_pos < idx_end {
613            let (name, chardata_id, required) = ATTRIBUTES[idx_start + cur_pos];
614            Some((name, &CHARACTER_DATA[chardata_id as usize], required))
615        } else {
616            None
617        }
618    }
619}
620
621/// Iterator over sub element definitions
622///
623/// returns the tuple (name: `ElementName`, etype: `ElementType`, `version_mask`: u32, `name_version_mask`: u32)
624pub struct SubelemDefinitionsIter {
625    type_id_stack: Vec<u16>,
626    indices: Vec<usize>,
627}
628
629impl Iterator for SubelemDefinitionsIter {
630    // ElementName, elementType, version_mask, is_named
631    type Item = (ElementName, ElementType, u32, u32);
632
633    fn next(&mut self) -> Option<Self::Item> {
634        if self.type_id_stack.is_empty() {
635            None
636        } else {
637            debug_assert_eq!(self.type_id_stack.len(), self.indices.len());
638
639            let depth = self.indices.len() - 1;
640            let current_type = self.type_id_stack[depth];
641            let cur_pos = self.indices[depth];
642            let (start_idx, end_idx) = ElementType::get_sub_element_idx(current_type);
643
644            if start_idx + cur_pos < end_idx {
645                match &SUBELEMENTS[start_idx + cur_pos] {
646                    SubElement::Element(idx) => {
647                        // found an element, return it and advance
648                        let name = ELEMENTS[*idx as usize].name;
649                        self.indices[depth] += 1;
650                        let ver_idx = ElementType::get_sub_element_ver(current_type);
651                        let version_mask = VERSION_INFO[ver_idx + cur_pos];
652                        let is_named = ElementType::new(*idx).short_name_version_mask().unwrap_or(0);
653                        Some((name, ElementType::new(*idx), version_mask, is_named))
654                    }
655                    SubElement::Group(groupid) => {
656                        // found a group, descend into it
657                        self.type_id_stack.push(*groupid);
658                        self.indices.push(0);
659                        self.next()
660                    }
661                }
662            } else {
663                // finished processing this element / group; remove it from the stack
664                self.indices.pop();
665                self.type_id_stack.pop();
666                if !self.indices.is_empty() {
667                    self.indices[depth - 1] += 1;
668                }
669                self.next()
670            }
671        }
672    }
673}
674
675// manually implement Debug for CharacterDataSpec; deriving it is not possible, because that fails on the check_fn field in ::Pattern.
676// The check_fn field is simply omitted here.
677impl core::fmt::Debug for CharacterDataSpec {
678    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
679        match self {
680            Self::Enum { items } => f.debug_struct("Enum").field("items", items).finish(),
681            Self::Pattern { regex, max_length, .. } => f
682                .debug_struct("Pattern")
683                .field("regex", regex)
684                .field("max_length", max_length)
685                .finish(),
686            Self::String {
687                preserve_whitespace,
688                max_length,
689            } => f
690                .debug_struct("String")
691                .field("preserve_whitespace", preserve_whitespace)
692                .field("max_length", max_length)
693                .finish(),
694            Self::UnsignedInteger => write!(f, "UnsignedInteger"),
695            Self::Float => write!(f, "Double"),
696        }
697    }
698}
699
700/// expand a version mask (u32) to a list of versions in the mask
701#[must_use]
702pub fn expand_version_mask(version_mask: u32) -> Vec<AutosarVersion> {
703    let mut versions = vec![];
704    for i in 0..u32::BITS {
705        let val = 1u32 << i;
706        if version_mask & val != 0
707            && let Some(enum_value) = AutosarVersion::from_val(val)
708        {
709            versions.push(enum_value);
710        }
711    }
712
713    versions
714}
715
716pub(crate) fn hashfunc(mut data: &[u8]) -> (u32, u32, u32) {
717    const HASHCONST1: u32 = 0x541C_69B2; // these 4 constant values are not special, just random values
718    const HASHCONST2: u32 = 0x3B17_161B;
719
720    let mut f1 = 0x3314_3C63_u32;
721    let mut f2 = 0x88B0_B21E_u32;
722    while data.len() >= 4 {
723        let val = u32::from_ne_bytes(data[..4].try_into().unwrap());
724        f1 = f1.rotate_left(5).bitxor(val).wrapping_mul(HASHCONST1);
725        f2 = f2.rotate_left(6).bitxor(val).wrapping_mul(HASHCONST2);
726        data = &data[4..];
727    }
728    if data.len() >= 2 {
729        let val = u32::from(u16::from_ne_bytes(data[..2].try_into().unwrap()));
730        f1 = f1.rotate_left(5).bitxor(val).wrapping_mul(HASHCONST1);
731        f2 = f2.rotate_left(6).bitxor(val).wrapping_mul(HASHCONST2);
732        data = &data[2..];
733    }
734    if !data.is_empty() {
735        f1 = f1.rotate_left(5).bitxor(u32::from(data[0])).wrapping_mul(HASHCONST1);
736        f2 = f2.rotate_left(6).bitxor(u32::from(data[0])).wrapping_mul(HASHCONST2);
737    }
738    let g = f1.bitxor(f2);
739    (g, f1, f2)
740}
741
742#[cfg(test)]
743mod test {
744    extern crate std;
745    use alloc::string::ToString;
746    use core::str::FromStr;
747    use num_traits::FromPrimitive;
748    use std::collections::HashSet;
749
750    use super::*;
751
752    fn get_prm_char_element_type() -> ElementType {
753        let (ar_packages_type, _) = ElementType::ROOT
754            .find_sub_element(ElementName::ArPackages, u32::MAX)
755            .unwrap();
756        let (ar_package_type, _) = ar_packages_type
757            .find_sub_element(ElementName::ArPackage, u32::MAX)
758            .unwrap();
759        let (elements_type, _) = ar_package_type
760            .find_sub_element(ElementName::Elements, u32::MAX)
761            .unwrap();
762        let (documentation_type, _) = elements_type
763            .find_sub_element(ElementName::Documentation, u32::MAX)
764            .unwrap();
765        let (documentation_content_type, _) = documentation_type
766            .find_sub_element(ElementName::DocumentationContent, u32::MAX)
767            .unwrap();
768        let (prms_type, _) = documentation_content_type
769            .find_sub_element(ElementName::Prms, u32::MAX)
770            .unwrap();
771        let (prm_type, _) = prms_type.find_sub_element(ElementName::Prm, u32::MAX).unwrap();
772        let (prm_char_type, _) = prm_type.find_sub_element(ElementName::PrmChar, u32::MAX).unwrap();
773
774        prm_char_type
775    }
776
777    #[test]
778    fn find_sub_element() {
779        let prm_char_type = get_prm_char_element_type();
780        let (_, indices) = prm_char_type.find_sub_element(ElementName::Tol, 0xffffffff).unwrap();
781        assert_eq!(indices, vec![1, 0, 0, 0, 1]);
782    }
783
784    #[test]
785    fn find_sub_element_version_dependent() {
786        let (ar_packages_type, _) = ElementType::ROOT
787            .find_sub_element(ElementName::ArPackages, u32::MAX)
788            .unwrap();
789        let (ar_package_type, _) = ar_packages_type
790            .find_sub_element(ElementName::ArPackage, u32::MAX)
791            .unwrap();
792        let (elements_type, _) = ar_package_type
793            .find_sub_element(ElementName::Elements, u32::MAX)
794            .unwrap();
795        let (sw_base_type_type, _) = elements_type
796            .find_sub_element(ElementName::SwBaseType, u32::MAX)
797            .unwrap();
798        let (_, indices) = sw_base_type_type
799            .find_sub_element(ElementName::BaseTypeSize, AutosarVersion::Autosar_4_0_1 as u32)
800            .unwrap();
801        assert_eq!(indices, vec![11, 0]);
802
803        let (_, indices) = sw_base_type_type
804            .find_sub_element(ElementName::BaseTypeSize, AutosarVersion::Autosar_4_1_1 as u32)
805            .unwrap();
806        assert_eq!(indices, vec![13]);
807    }
808
809    #[test]
810    fn get_sub_element_spec() {
811        let prm_char_type = get_prm_char_element_type();
812        let (abs_type, indices) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
813        let sub_elem_spec = prm_char_type.get_sub_element_spec(&indices);
814        let (sub_element, _) = sub_elem_spec.unwrap();
815        if let SubElement::Element(idx) = sub_element {
816            let name = ELEMENTS[*idx as usize].name;
817            assert_eq!(name, ElementName::Abs);
818            assert_eq!(ElementType::new(*idx), abs_type);
819        }
820
821        // the element_indices passed to get_sub_element_spec may not be empty
822        let sub_elem_spec2 = prm_char_type.get_sub_element_spec(&[]);
823        assert!(sub_elem_spec2.is_none());
824        // element_indices is nonsense
825        let sub_elem_spec2 = prm_char_type.get_sub_element_spec(&[0, 0, 0, 0, 0, 0, 0, 0, 0]);
826        assert!(sub_elem_spec2.is_none());
827    }
828
829    #[test]
830    fn get_sub_element_version_mask() {
831        let prm_char_type = get_prm_char_element_type();
832        let (_, indices) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
833        let sub_elem_spec = prm_char_type.get_sub_element_spec(&indices).unwrap();
834        let version_mask2 = prm_char_type.get_sub_element_version_mask(&indices).unwrap();
835        let (_, version_mask) = sub_elem_spec;
836        assert_eq!(version_mask, version_mask2);
837
838        let no_result = prm_char_type.get_sub_element_version_mask(&[]);
839        assert!(no_result.is_none());
840    }
841
842    #[test]
843    fn get_sub_element_multiplicity() {
844        let prm_char_type = get_prm_char_element_type();
845        let (_, indices) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
846        let sub_elem_spec = prm_char_type.get_sub_element_spec(&indices).unwrap().0;
847        let multiplicity2 = prm_char_type.get_sub_element_multiplicity(&indices).unwrap();
848        if let SubElement::Element(idx) = sub_elem_spec {
849            let multiplicity = ELEMENTS[*idx as usize].multiplicity;
850            assert_eq!(multiplicity, multiplicity2);
851        }
852
853        let no_result = prm_char_type.get_sub_element_multiplicity(&[]);
854        assert!(no_result.is_none());
855    }
856
857    #[test]
858    fn get_sub_element_container_mode() {
859        let prm_char_type = get_prm_char_element_type();
860        let (_, indices) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
861        let mode = prm_char_type.get_sub_element_container_mode(&indices);
862        assert_eq!(mode, ContentMode::Sequence);
863    }
864
865    #[test]
866    fn find_common_group() {
867        let prm_char_type = get_prm_char_element_type();
868        let (_, indices_abs) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
869        let (_, indices_tol) = prm_char_type.find_sub_element(ElementName::Tol, u32::MAX).unwrap();
870        let (_, indices_min) = prm_char_type.find_sub_element(ElementName::Min, u32::MAX).unwrap();
871        // see the documentation on find_sub_element for the complex structure under PRM-CHAR
872        // ABS and TOL share a sequence group (top level)
873        let group1 = prm_char_type.find_common_group(&indices_abs, &indices_tol);
874        assert_eq!(group1.content_mode(), ContentMode::Sequence);
875        // ABS and MIN have the second level choice group in common
876        let group2 = prm_char_type.find_common_group(&indices_abs, &indices_min);
877        assert_eq!(group2.content_mode(), ContentMode::Choice);
878    }
879
880    #[test]
881    fn find_attribute_spec() {
882        let AttributeSpec {
883            spec,
884            required,
885            version,
886        } = ElementType::ROOT.find_attribute_spec(AttributeName::xmlns).unwrap();
887        let spec_dbgstr = format!("{:#?}", spec);
888        assert!(!spec_dbgstr.is_empty());
889        // xmlns in AUTOSAR is required
890        assert!(required);
891        // must be specified both in the first and latest versions (and every one in between - not tested)
892        assert_ne!(version & AutosarVersion::Autosar_00050 as u32, 0);
893        assert_ne!(version & AutosarVersion::Autosar_4_0_1 as u32, 0);
894    }
895
896    #[test]
897    fn subelement_definition_iterator() {
898        let (ar_packages_type, _) = ElementType::ROOT
899            .find_sub_element(ElementName::ArPackages, u32::MAX)
900            .unwrap();
901        let (ar_package_type, _) = ar_packages_type
902            .find_sub_element(ElementName::ArPackage, u32::MAX)
903            .unwrap();
904        let (elements_type, _) = ar_package_type
905            .find_sub_element(ElementName::Elements, u32::MAX)
906            .unwrap();
907
908        let se_iter = elements_type.sub_element_spec_iter();
909        assert_eq!(se_iter.count(), 692); // this test breaks when support for new versions is added
910
911        let prm_char_type = get_prm_char_element_type();
912        let pc_iter = prm_char_type.sub_element_spec_iter();
913        // not all items in the sub element spec are compatible with the latest Autosar version, count only the ones that are compatible
914        let compatible_count = pc_iter
915            .filter(|(_, _, version_mask, _)| AutosarVersion::Autosar_00050.compatible(*version_mask))
916            .count();
917        assert_eq!(compatible_count, 9);
918    }
919
920    #[test]
921    fn autosar_version() {
922        // does from_str work correctly?
923        assert_eq!(
924            AutosarVersion::from_str("AUTOSAR_4-0-1.xsd").unwrap(),
925            AutosarVersion::Autosar_4_0_1
926        );
927        assert_eq!(
928            AutosarVersion::from_str("AUTOSAR_4-0-2.xsd").unwrap(),
929            AutosarVersion::Autosar_4_0_2
930        );
931        assert_eq!(
932            AutosarVersion::from_str("AUTOSAR_4-0-3.xsd").unwrap(),
933            AutosarVersion::Autosar_4_0_3
934        );
935        assert_eq!(
936            AutosarVersion::from_str("AUTOSAR_4-1-1.xsd").unwrap(),
937            AutosarVersion::Autosar_4_1_1
938        );
939        assert_eq!(
940            AutosarVersion::from_str("AUTOSAR_4-1-2.xsd").unwrap(),
941            AutosarVersion::Autosar_4_1_2
942        );
943        assert_eq!(
944            AutosarVersion::from_str("AUTOSAR_4-1-3.xsd").unwrap(),
945            AutosarVersion::Autosar_4_1_3
946        );
947        assert_eq!(
948            AutosarVersion::from_str("AUTOSAR_4-2-1.xsd").unwrap(),
949            AutosarVersion::Autosar_4_2_1
950        );
951        assert_eq!(
952            AutosarVersion::from_str("AUTOSAR_4-2-2.xsd").unwrap(),
953            AutosarVersion::Autosar_4_2_2
954        );
955        assert_eq!(
956            AutosarVersion::from_str("AUTOSAR_4-3-0.xsd").unwrap(),
957            AutosarVersion::Autosar_4_3_0
958        );
959        assert_eq!(
960            AutosarVersion::from_str("AUTOSAR_00042.xsd").unwrap(),
961            AutosarVersion::Autosar_00042
962        );
963        assert_eq!(
964            AutosarVersion::from_str("AUTOSAR_00043.xsd").unwrap(),
965            AutosarVersion::Autosar_00043
966        );
967        assert_eq!(
968            AutosarVersion::from_str("AUTOSAR_00044.xsd").unwrap(),
969            AutosarVersion::Autosar_00044
970        );
971        assert_eq!(
972            AutosarVersion::from_str("AUTOSAR_00045.xsd").unwrap(),
973            AutosarVersion::Autosar_00045
974        );
975        assert_eq!(
976            AutosarVersion::from_str("AUTOSAR_00046.xsd").unwrap(),
977            AutosarVersion::Autosar_00046
978        );
979        assert_eq!(
980            AutosarVersion::from_str("AUTOSAR_00047.xsd").unwrap(),
981            AutosarVersion::Autosar_00047
982        );
983        assert_eq!(
984            AutosarVersion::from_str("AUTOSAR_00048.xsd").unwrap(),
985            AutosarVersion::Autosar_00048
986        );
987        assert_eq!(
988            AutosarVersion::from_str("AUTOSAR_00049.xsd").unwrap(),
989            AutosarVersion::Autosar_00049
990        );
991        assert_eq!(
992            AutosarVersion::from_str("AUTOSAR_00050.xsd").unwrap(),
993            AutosarVersion::Autosar_00050
994        );
995        assert_eq!(
996            AutosarVersion::from_str("AUTOSAR_00051.xsd").unwrap(),
997            AutosarVersion::Autosar_00051
998        );
999        assert_eq!(
1000            AutosarVersion::from_str("AUTOSAR_00052.xsd").unwrap(),
1001            AutosarVersion::Autosar_00052
1002        );
1003        assert_eq!(
1004            AutosarVersion::from_str("AUTOSAR_00053.xsd").unwrap(),
1005            AutosarVersion::Autosar_00053
1006        );
1007        assert_eq!(
1008            AutosarVersion::from_str("AUTOSAR_00054.xsd").unwrap(),
1009            AutosarVersion::Autosar_00054
1010        );
1011
1012        // do all the version descriptions exist & make sense?
1013        assert!(AutosarVersion::Autosar_4_0_1.describe().starts_with("AUTOSAR"));
1014        assert!(AutosarVersion::Autosar_4_0_2.describe().starts_with("AUTOSAR"));
1015        assert!(AutosarVersion::Autosar_4_0_3.describe().starts_with("AUTOSAR"));
1016        assert!(AutosarVersion::Autosar_4_1_1.describe().starts_with("AUTOSAR"));
1017        assert!(AutosarVersion::Autosar_4_1_2.describe().starts_with("AUTOSAR"));
1018        assert!(AutosarVersion::Autosar_4_1_3.describe().starts_with("AUTOSAR"));
1019        assert!(AutosarVersion::Autosar_4_2_1.describe().starts_with("AUTOSAR"));
1020        assert!(AutosarVersion::Autosar_4_2_2.describe().starts_with("AUTOSAR"));
1021        assert!(AutosarVersion::Autosar_4_3_0.describe().starts_with("AUTOSAR"));
1022        assert!(AutosarVersion::Autosar_00042.describe().starts_with("AUTOSAR"));
1023        assert!(AutosarVersion::Autosar_00043.describe().starts_with("AUTOSAR"));
1024        assert!(AutosarVersion::Autosar_00044.describe().starts_with("AUTOSAR"));
1025        assert!(AutosarVersion::Autosar_00045.describe().starts_with("AUTOSAR"));
1026        assert!(AutosarVersion::Autosar_00046.describe().starts_with("AUTOSAR"));
1027        assert!(AutosarVersion::Autosar_00047.describe().starts_with("AUTOSAR"));
1028        assert!(AutosarVersion::Autosar_00048.describe().starts_with("AUTOSAR"));
1029        assert!(AutosarVersion::Autosar_00049.describe().starts_with("AUTOSAR"));
1030        assert!(AutosarVersion::Autosar_00050.describe().starts_with("AUTOSAR"));
1031        assert!(AutosarVersion::Autosar_00051.describe().starts_with("AUTOSAR"));
1032        assert!(AutosarVersion::Autosar_00052.describe().starts_with("AUTOSAR"));
1033        assert!(AutosarVersion::Autosar_00053.describe().starts_with("AUTOSAR"));
1034        assert!(AutosarVersion::Autosar_00054.describe().starts_with("AUTOSAR"));
1035
1036        // do all the xsd file names exist?
1037        assert!(AutosarVersion::Autosar_4_0_1.filename().ends_with(".xsd"));
1038        assert!(AutosarVersion::Autosar_4_0_2.filename().ends_with(".xsd"));
1039        assert!(AutosarVersion::Autosar_4_0_3.filename().ends_with(".xsd"));
1040        assert!(AutosarVersion::Autosar_4_1_1.filename().ends_with(".xsd"));
1041        assert!(AutosarVersion::Autosar_4_1_2.filename().ends_with(".xsd"));
1042        assert!(AutosarVersion::Autosar_4_1_3.filename().ends_with(".xsd"));
1043        assert!(AutosarVersion::Autosar_4_2_1.filename().ends_with(".xsd"));
1044        assert!(AutosarVersion::Autosar_4_2_2.filename().ends_with(".xsd"));
1045        assert!(AutosarVersion::Autosar_4_3_0.filename().ends_with(".xsd"));
1046        assert!(AutosarVersion::Autosar_00042.filename().ends_with(".xsd"));
1047        assert!(AutosarVersion::Autosar_00043.filename().ends_with(".xsd"));
1048        assert!(AutosarVersion::Autosar_00044.filename().ends_with(".xsd"));
1049        assert!(AutosarVersion::Autosar_00045.filename().ends_with(".xsd"));
1050        assert!(AutosarVersion::Autosar_00046.filename().ends_with(".xsd"));
1051        assert!(AutosarVersion::Autosar_00047.filename().ends_with(".xsd"));
1052        assert!(AutosarVersion::Autosar_00048.filename().ends_with(".xsd"));
1053        assert!(AutosarVersion::Autosar_00049.filename().ends_with(".xsd"));
1054        assert!(AutosarVersion::Autosar_00050.filename().ends_with(".xsd"));
1055        assert!(AutosarVersion::Autosar_00051.filename().ends_with(".xsd"));
1056        assert!(AutosarVersion::Autosar_00052.filename().ends_with(".xsd"));
1057        assert!(AutosarVersion::Autosar_00053.filename().ends_with(".xsd"));
1058        assert!(AutosarVersion::Autosar_00054.filename().ends_with(".xsd"));
1059
1060        // to_string() should give the same result as describe()
1061        assert_eq!(
1062            AutosarVersion::Autosar_4_0_1.to_string(),
1063            AutosarVersion::Autosar_4_0_1.describe()
1064        );
1065
1066        // clone impl exists
1067        let cloned = AutosarVersion::Autosar_00050;
1068        assert_eq!(cloned, AutosarVersion::Autosar_00050);
1069
1070        // version parse error
1071        let error = AutosarVersion::from_str("something else");
1072        assert_eq!(format!("{:#?}", error.unwrap_err()), "ParseAutosarVersionError");
1073
1074        //Autosar version implements Hash and can be inserted into HashSet / HashMap
1075        let mut hashset = HashSet::<AutosarVersion>::new();
1076        hashset.insert(AutosarVersion::Autosar_00050);
1077    }
1078
1079    #[test]
1080    fn attribute_name_basics() {
1081        // attribute name round trip: enum -> str -> enum
1082        assert_eq!(
1083            AttributeName::Uuid,
1084            AttributeName::from_str(AttributeName::Uuid.to_str()).unwrap()
1085        );
1086
1087        // to_string()
1088        assert_eq!(AttributeName::Uuid.to_string(), "UUID");
1089
1090        // clone impl exists
1091        let cloned = AttributeName::Uuid;
1092        assert_eq!(cloned, AttributeName::Uuid);
1093
1094        // attribute parse error
1095        let error = AttributeName::from_str("unknown attribute name");
1096        assert_eq!(format!("{:#?}", error.unwrap_err()), "ParseAttributeNameError");
1097
1098        // attribute names implement Hash and can be inserted into HashSet / HashMap
1099        let mut hashset = HashSet::<AttributeName>::new();
1100        hashset.insert(AttributeName::Dest);
1101    }
1102
1103    #[test]
1104    fn element_name_basics() {
1105        // element name round trip: enum -> str -> enum
1106        assert_eq!(
1107            ElementName::Autosar,
1108            ElementName::from_str(ElementName::Autosar.to_str()).unwrap()
1109        );
1110
1111        // to_string()
1112        assert_eq!(ElementName::Autosar.to_string(), "AUTOSAR");
1113
1114        // clone impl exists
1115        let cloned = ElementName::Autosar;
1116        assert_eq!(cloned, ElementName::Autosar);
1117
1118        // element name parse error
1119        let error = ElementName::from_str("unknown element name");
1120        assert_eq!(format!("{:#?}", error.unwrap_err()), "ParseElementNameError");
1121
1122        // element names implement Hash and can be inserted into HashSet / HashMap
1123        let mut hashset = HashSet::<ElementName>::new();
1124        hashset.insert(ElementName::Autosar);
1125    }
1126
1127    #[test]
1128    fn enum_item_basics() {
1129        // enum item round trip: enum -> str -> enum
1130        assert_eq!(
1131            EnumItem::Default,
1132            EnumItem::from_str(EnumItem::Default.to_str()).unwrap()
1133        );
1134
1135        // to_string()
1136        assert_eq!(EnumItem::Default.to_string(), "DEFAULT");
1137
1138        // clone impl exists
1139        let cloned = EnumItem::Abstract;
1140        assert_eq!(cloned, EnumItem::Abstract);
1141
1142        // enum item parse error
1143        let error = EnumItem::from_str("unknown enum item");
1144        assert_eq!(format!("{:#?}", error.unwrap_err()), "ParseEnumItemError");
1145
1146        // enum items implement Hash and can be inserted into HashSet / HashMap
1147        let mut hashset = HashSet::<EnumItem>::new();
1148        hashset.insert(EnumItem::Abstract);
1149    }
1150
1151    #[test]
1152    fn ordered() {
1153        let (ar_packages_type, _) = ElementType::ROOT
1154            .find_sub_element(ElementName::ArPackages, u32::MAX)
1155            .unwrap();
1156        let (ar_package_type, _) = ar_packages_type
1157            .find_sub_element(ElementName::ArPackage, u32::MAX)
1158            .unwrap();
1159        let (elements_type, _) = ar_package_type
1160            .find_sub_element(ElementName::Elements, u32::MAX)
1161            .unwrap();
1162        // BSW-MODULE-ENTRY: This class represents a single API entry (C-function prototype) into the BSW module or cluster.
1163        let (bsw_module_entry, _) = elements_type
1164            .find_sub_element(ElementName::BswModuleEntry, u32::MAX)
1165            .unwrap();
1166        // ARGUMENTS: Arguments belonging of the BswModuleEntry.
1167        let (arguments, _) = bsw_module_entry
1168            .find_sub_element(ElementName::Arguments, u32::MAX)
1169            .unwrap();
1170
1171        assert!(!bsw_module_entry.is_ordered());
1172        assert!(arguments.is_ordered());
1173    }
1174
1175    #[test]
1176    fn splittable() {
1177        let (ar_packages_type, _) = ElementType::ROOT
1178            .find_sub_element(ElementName::ArPackages, u32::MAX)
1179            .unwrap();
1180        let (ar_package_type, _) = ar_packages_type
1181            .find_sub_element(ElementName::ArPackage, u32::MAX)
1182            .unwrap();
1183        let (elements_type, _) = ar_package_type
1184            .find_sub_element(ElementName::Elements, u32::MAX)
1185            .unwrap();
1186
1187        assert!(!ar_package_type.splittable_in(AutosarVersion::Autosar_00051));
1188        assert_ne!(ar_packages_type.splittable() & AutosarVersion::Autosar_00051 as u32, 0);
1189        assert!(ar_packages_type.splittable_in(AutosarVersion::Autosar_00051));
1190        assert_ne!(elements_type.splittable() & AutosarVersion::Autosar_00051 as u32, 0);
1191    }
1192
1193    #[test]
1194    fn std_restriction() {
1195        let (ar_packages_type, _) = ElementType::ROOT
1196            .find_sub_element(ElementName::ArPackages, u32::MAX)
1197            .unwrap();
1198        let (ar_package_type, _) = ar_packages_type
1199            .find_sub_element(ElementName::ArPackage, u32::MAX)
1200            .unwrap();
1201        let (elements_type, _) = ar_package_type
1202            .find_sub_element(ElementName::Elements, u32::MAX)
1203            .unwrap();
1204        let (machine_type, _) = elements_type.find_sub_element(ElementName::Machine, u32::MAX).unwrap();
1205        let (defapp_timeout_type, _) = machine_type
1206            .find_sub_element(ElementName::DefaultApplicationTimeout, u32::MAX)
1207            .unwrap();
1208
1209        assert_eq!(ar_package_type.std_restriction(), StdRestrict::NotRestricted);
1210        assert_eq!(defapp_timeout_type.std_restriction(), StdRestrict::AdaptivePlatform);
1211    }
1212
1213    #[test]
1214    fn reference_dest() {
1215        let (ar_packages_type, _) = ElementType::ROOT
1216            .find_sub_element(ElementName::ArPackages, u32::MAX)
1217            .unwrap();
1218        let (ar_package_type, _) = ar_packages_type
1219            .find_sub_element(ElementName::ArPackage, u32::MAX)
1220            .unwrap();
1221        let (elements_type, _) = ar_package_type
1222            .find_sub_element(ElementName::Elements, u32::MAX)
1223            .unwrap();
1224        let (can_tp_config_type, _) = elements_type
1225            .find_sub_element(ElementName::CanTpConfig, u32::MAX)
1226            .unwrap();
1227        let (tp_connections_type, _) = can_tp_config_type
1228            .find_sub_element(ElementName::TpConnections, u32::MAX)
1229            .unwrap();
1230        let (can_tp_connection_type, _) = tp_connections_type
1231            .find_sub_element(ElementName::CanTpConnection, u32::MAX)
1232            .unwrap();
1233        let (ident_type, _) = can_tp_connection_type
1234            .find_sub_element(ElementName::Ident, u32::MAX)
1235            .unwrap();
1236
1237        let (diagnostic_connection_type, _) = elements_type
1238            .find_sub_element(ElementName::DiagnosticConnection, u32::MAX)
1239            .unwrap();
1240        let (physical_request_ref_type, _) = diagnostic_connection_type
1241            .find_sub_element(ElementName::PhysicalRequestRef, u32::MAX)
1242            .unwrap();
1243
1244        let ref_value = physical_request_ref_type.reference_dest_value(&ident_type).unwrap();
1245        assert_eq!(ref_value, EnumItem::TpConnectionIdent);
1246        assert!(ident_type.verify_reference_dest(ref_value));
1247        let invalid_ref = physical_request_ref_type.reference_dest_value(&tp_connections_type);
1248        assert!(invalid_ref.is_none());
1249    }
1250
1251    #[test]
1252    fn traits() {
1253        // this test is basically nonsense - derived traits should all be ok
1254        // but there is no way to exclude them from coverage
1255        // ElementMultiplicity: Debug & Clone
1256        let mult = ElementMultiplicity::Any;
1257        let m2 = mult; // must be .clone(), otherwise the copy impl is tested instead
1258        assert_eq!(format!("{:#?}", mult), format!("{:#?}", m2));
1259
1260        // ContentMode: Debug, Clone
1261        let cm = ContentMode::Sequence;
1262        let cm2 = cm; // must be .clone(), otherwise the copy impl is tested instead
1263        assert_eq!(format!("{:#?}", cm), format!("{:#?}", cm2));
1264
1265        // ElementType: Debug, Clone, Eq & Hash
1266        let et = ElementType::ROOT;
1267        let et2 = et; // must be .clone(), otherwise the copy impl is tested instead
1268        assert_eq!(format!("{:#?}", et), format!("{:#?}", et2));
1269        let mut hashset = HashSet::<ElementType>::new();
1270        hashset.insert(et);
1271        let inserted = hashset.insert(et2);
1272        assert!(!inserted);
1273
1274        // AutosarVersion: Debug, Clone, Hash
1275        let ver = AutosarVersion::LATEST;
1276        let ver2 = ver; // must be .clone(), otherwise the copy impl is tested instead
1277        assert_eq!(format!("{ver:#?}"), format!("{ver2:#?}"));
1278        let mut hashset = HashSet::<AutosarVersion>::new();
1279        hashset.insert(ver);
1280        let inserted = hashset.insert(ver2);
1281        assert!(!inserted);
1282
1283        // ElementName: Debug, Clone, Hash
1284        let en = ElementName::Autosar;
1285        let en2 = en; // must be .clone(), otherwise the copy impl is tested instead
1286        assert_eq!(format!("{en:#?}"), format!("{en2:#?}"));
1287        let mut hashset = HashSet::<ElementName>::new();
1288        hashset.insert(en);
1289        let inserted = hashset.insert(en2);
1290        assert!(!inserted);
1291
1292        // CharacterDataSpec: Debug
1293        let cdata_spec = CharacterDataSpec::String {
1294            preserve_whitespace: true,
1295            max_length: None,
1296        };
1297        let txt = format!("{cdata_spec:#?}");
1298        assert!(txt.starts_with("String"));
1299        let cdata_spec = CharacterDataSpec::Pattern {
1300            check_fn: crate::regex::validate_regex_1,
1301            regex: r"0x[0-9a-z]*",
1302            max_length: None,
1303        };
1304        let txt = format!("{cdata_spec:#?}");
1305        assert!(txt.starts_with("Pattern"));
1306        let cdata_spec = CharacterDataSpec::Enum {
1307            items: &[(EnumItem::Custom, 0x7e000)],
1308        };
1309        let txt = format!("{cdata_spec:#?}");
1310        assert!(txt.starts_with("Enum"));
1311        let cdata_spec = CharacterDataSpec::Float;
1312        let txt = format!("{cdata_spec:#?}");
1313        assert!(txt.starts_with("Double"));
1314        let cdata_spec = CharacterDataSpec::UnsignedInteger;
1315        let txt = format!("{cdata_spec:#?}");
1316        assert!(txt.starts_with("UnsignedInteger"));
1317    }
1318
1319    #[test]
1320    fn test_expand_version_mask() {
1321        let (ar_packages_type, _) = ElementType::ROOT
1322            .find_sub_element(ElementName::ArPackages, u32::MAX)
1323            .unwrap();
1324        let (ar_package_type, _) = ar_packages_type
1325            .find_sub_element(ElementName::ArPackage, u32::MAX)
1326            .unwrap();
1327        let (elements_type, _) = ar_package_type
1328            .find_sub_element(ElementName::Elements, u32::MAX)
1329            .unwrap();
1330        let (_, element_indices) = elements_type
1331            .find_sub_element(ElementName::AdaptiveApplicationSwComponentType, u32::MAX)
1332            .unwrap();
1333        let version_mask = elements_type.get_sub_element_version_mask(&element_indices).unwrap();
1334
1335        assert_eq!(
1336            &[
1337                AutosarVersion::Autosar_00042,
1338                AutosarVersion::Autosar_00043,
1339                AutosarVersion::Autosar_00044,
1340                AutosarVersion::Autosar_00045,
1341                AutosarVersion::Autosar_00046,
1342                AutosarVersion::Autosar_00047,
1343                AutosarVersion::Autosar_00048,
1344                AutosarVersion::Autosar_00049,
1345                AutosarVersion::Autosar_00050,
1346                AutosarVersion::Autosar_00051,
1347                AutosarVersion::Autosar_00052,
1348                AutosarVersion::Autosar_00053,
1349                AutosarVersion::Autosar_00054,
1350            ],
1351            &*expand_version_mask(version_mask)
1352        );
1353    }
1354
1355    #[test]
1356    fn test_version_masks() {
1357        assert_eq!(AutosarVersion::from_u64(0x1), Some(AutosarVersion::Autosar_4_0_1));
1358        assert_eq!(AutosarVersion::from_u64(0x2), Some(AutosarVersion::Autosar_4_0_2));
1359        assert_eq!(AutosarVersion::from_u64(0x4), Some(AutosarVersion::Autosar_4_0_3));
1360        assert_eq!(AutosarVersion::from_u64(0x8), Some(AutosarVersion::Autosar_4_1_1));
1361        assert_eq!(AutosarVersion::from_u64(0x10), Some(AutosarVersion::Autosar_4_1_2));
1362        assert_eq!(AutosarVersion::from_u64(0x20), Some(AutosarVersion::Autosar_4_1_3));
1363        assert_eq!(AutosarVersion::from_u64(0x40), Some(AutosarVersion::Autosar_4_2_1));
1364        assert_eq!(AutosarVersion::from_u64(0x80), Some(AutosarVersion::Autosar_4_2_2));
1365        assert_eq!(AutosarVersion::from_u64(0x100), Some(AutosarVersion::Autosar_4_3_0));
1366        assert_eq!(AutosarVersion::from_u64(0x200), Some(AutosarVersion::Autosar_00042));
1367        assert_eq!(AutosarVersion::from_u64(0x400), Some(AutosarVersion::Autosar_00043));
1368        assert_eq!(AutosarVersion::from_u64(0x800), Some(AutosarVersion::Autosar_00044));
1369        assert_eq!(AutosarVersion::from_u64(0x1000), Some(AutosarVersion::Autosar_00045));
1370        assert_eq!(AutosarVersion::from_u64(0x2000), Some(AutosarVersion::Autosar_00046));
1371        assert_eq!(AutosarVersion::from_u64(0x4000), Some(AutosarVersion::Autosar_00047));
1372        assert_eq!(AutosarVersion::from_u64(0x8000), Some(AutosarVersion::Autosar_00048));
1373        assert_eq!(AutosarVersion::from_u64(0x10000), Some(AutosarVersion::Autosar_00049));
1374        assert_eq!(AutosarVersion::from_u64(0x20000), Some(AutosarVersion::Autosar_00050));
1375        assert_eq!(AutosarVersion::from_u64(0x40000), Some(AutosarVersion::Autosar_00051));
1376        assert_eq!(AutosarVersion::from_u64(0x80000), Some(AutosarVersion::Autosar_00052));
1377        assert_eq!(AutosarVersion::from_u64(0x100000), Some(AutosarVersion::Autosar_00053));
1378        assert_eq!(AutosarVersion::from_u64(0x200000), Some(AutosarVersion::Autosar_00054));
1379        // invalid version mask: more than one bit set
1380        assert_eq!(AutosarVersion::from_u64(0xF), None);
1381
1382        // FromPrimitive also provides from_i64
1383        assert_eq!(AutosarVersion::from_i64(0x1), Some(AutosarVersion::Autosar_4_0_1));
1384        assert_eq!(AutosarVersion::from_i64(-1), None);
1385    }
1386
1387    #[cfg(feature = "docstrings")]
1388    #[test]
1389    fn test_docstring() {
1390        let (ar_packages_type, _) = ElementType::ROOT
1391            .find_sub_element(ElementName::ArPackages, u32::MAX)
1392            .unwrap();
1393        let docstring = ar_packages_type.docstring();
1394        assert_eq!(docstring, "This is the top level package in an AUTOSAR model.");
1395    }
1396}