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            if let SubElement::Element(idx) = sub_elements[0] {
412                if ELEMENTS[idx as usize].name == ElementName::ShortName {
413                    let ver_idx = ElementType::get_sub_element_ver(self.typ);
414                    return Some(VERSION_INFO[ver_idx]);
415                }
416            }
417        }
418        None
419    }
420
421    /// are elements of this elementType named in the given Autosar version
422    ///
423    /// Named elements must have a SHORT-NAME sub element. For some elements this
424    /// depends on the Autosar version.
425    ///
426    /// One example of this is END-2-END-METHOD-PROTECTION-PROPS, which was first
427    /// defined in `Autosar_00048`, but only has a name in `Autosar_00050`.
428    #[must_use]
429    pub fn is_named_in_version(&self, version: AutosarVersion) -> bool {
430        self.short_name_version_mask()
431            .is_some_and(|ver_mask| version.compatible(ver_mask))
432    }
433
434    /// is the `ElementType` a reference
435    #[must_use]
436    pub fn is_ref(&self) -> bool {
437        if let Some(idx) = DATATYPES[self.typ as usize].character_data {
438            idx == REFERENCE_TYPE_IDX
439        } else {
440            false
441        }
442    }
443
444    /// get the content mode for this `ElementType`
445    #[must_use]
446    pub const fn content_mode(&self) -> ContentMode {
447        DATATYPES[self.typ as usize].mode
448    }
449
450    /// get the character data spec for this `ElementType`
451    #[must_use]
452    pub const fn chardata_spec(&self) -> Option<&'static CharacterDataSpec> {
453        if let Some(chardata_id) = DATATYPES[self.typ as usize].character_data {
454            Some(&CHARACTER_DATA[chardata_id as usize])
455        } else {
456            None
457        }
458    }
459
460    /// find the spec for a single attribute by name
461    #[must_use]
462    pub fn find_attribute_spec(&self, attrname: AttributeName) -> Option<AttributeSpec> {
463        let (idx_start, idx_end) = ElementType::get_attributes_idx(self.typ);
464        let attributes = &ATTRIBUTES[idx_start..idx_end];
465        if let Some((find_pos, (_, chardata_id, required))) =
466            attributes.iter().enumerate().find(|(_, (name, ..))| *name == attrname)
467        {
468            let idx_ver_start = ElementType::get_attributes_ver(self.typ);
469            let version = VERSION_INFO[idx_ver_start + find_pos];
470            Some(AttributeSpec {
471                spec: &CHARACTER_DATA[*chardata_id as usize],
472                required: *required,
473                version,
474            })
475        } else {
476            None
477        }
478    }
479
480    /// create an iterator over all attribute definitions in the current `ElementType`
481    #[must_use]
482    pub const fn attribute_spec_iter(&self) -> AttrDefinitionsIter {
483        AttrDefinitionsIter {
484            type_id: self.typ,
485            pos: 0,
486        }
487    }
488
489    /// create an iterator over all sub elements of the current `ElementType`
490    #[must_use]
491    pub fn sub_element_spec_iter(&self) -> SubelemDefinitionsIter {
492        SubelemDefinitionsIter {
493            type_id_stack: vec![self.typ],
494            indices: vec![0],
495        }
496    }
497
498    /// Is this `ElementType` ordered
499    ///
500    /// It this is true, then the position of the sub elements of this element is semantically meaningful
501    /// and they may not be sorted / re-ordered without changing the meaning of the file.
502    ///
503    /// An example of this is ARGUMENTS in BSW-MODULE-ENTRY. ARGUMENTS is ordered, because each of its
504    /// SW-SERVICE-ARG sub elements represents a function argument
505    #[must_use]
506    pub const fn is_ordered(&self) -> bool {
507        ELEMENTS[self.def as usize].ordered
508    }
509
510    /// Is this `ElementType` splittable
511    ///
512    /// This function returns a bitfield that indicates in which versions (if any) the `ElementType` is marked as splittable.
513    /// A splittable element may be split across multiple arxml files
514    #[must_use]
515    pub const fn splittable(&self) -> u32 {
516        ELEMENTS[self.def as usize].splittable
517    }
518
519    /// Is the current `ElementType` splittable in the given version
520    ///
521    /// A splittable element may be split across multiple arxml files
522    #[must_use]
523    pub const fn splittable_in(&self, version: AutosarVersion) -> bool {
524        (ELEMENTS[self.def as usize].splittable & (version as u32)) != 0
525    }
526
527    /// Is this `ElementType` restricted to a particular edition of the Autosar standard
528    ///
529    /// Returns an [`StdRestrict`] enum, whose values are `ClassicPlatform`, `AdaptivePlatform`, `NotRestricted`
530    #[must_use]
531    pub const fn std_restriction(&self) -> StdRestrict {
532        ELEMENTS[self.def as usize].restrict_std
533    }
534
535    /// find the correct `EnumItem` to use in the DEST attribute when referring from this element to the other element
536    ///
537    /// Returns `Some(enum_item)` if the reference is possible, and None otherwise.
538    ///
539    /// Example:
540    ///
541    /// When referring to a `<CAN-TP-CONNECTION><IDENT><SHORT-NAME>foo...`
542    /// the referrring `<PHYSICAL-REQUEST-REF [...]>` must set DEST="TP-CONNECTION-IDENT"
543    #[must_use]
544    pub fn reference_dest_value(&self, other: &ElementType) -> Option<EnumItem> {
545        // this element must be a reference, and the other element must be identifiable, otherwise it is not a valid target
546        if self.is_ref() && other.is_named() {
547            let dest_spec = self.find_attribute_spec(AttributeName::Dest)?.spec;
548            if let CharacterDataSpec::Enum { items } = dest_spec {
549                let (start, end) = DATATYPES[other.typ as usize].ref_info;
550                let ref_by = &REF_ITEMS[start as usize..end as usize];
551                for ref_target_value in ref_by {
552                    for (enumitem, _) in *items {
553                        if ref_target_value == enumitem {
554                            return Some(*ref_target_value);
555                        }
556                    }
557                }
558            }
559        }
560        None
561    }
562
563    /// verify that the given `dest_value` is a valid enum item that can be used to refer to this element type
564    #[must_use]
565    pub fn verify_reference_dest(&self, dest_value: EnumItem) -> bool {
566        let (start, end) = DATATYPES[self.typ as usize].ref_info;
567        let values = &REF_ITEMS[start as usize..end as usize];
568        values.contains(&dest_value)
569    }
570
571    #[cfg(feature = "docstrings")]
572    #[must_use]
573    pub const fn docstring(&self) -> &str {
574        if let Some(docstring_id) = ELEMENTS[self.def as usize].docstring {
575            specification::ELEMENT_DOCSTRINGS[docstring_id as usize]
576        } else {
577            ""
578        }
579    }
580
581    /// `ElementType::ROOT` is the root `ElementType` of the Autosar arxml document: this is the `ElementType` of the AUTOSAR element
582    pub const ROOT: Self = ElementType::new(AUTOSAR_ELEMENT);
583}
584
585/// custom implementation of Debug for ElementType - make the output more compact, since the long form is not very useful
586impl core::fmt::Debug for ElementType {
587    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
588        write!(f, "ElementType({}, {})", self.def, self.typ)
589    }
590}
591
592impl GroupType {
593    /// get the content mode for this `GroupType`
594    #[must_use]
595    pub const fn content_mode(&self) -> ContentMode {
596        DATATYPES[self.0 as usize].mode
597    }
598}
599
600/// Iterator for attribute definitions
601pub struct AttrDefinitionsIter {
602    type_id: u16,
603    pos: usize,
604}
605
606impl Iterator for AttrDefinitionsIter {
607    type Item = (AttributeName, &'static CharacterDataSpec, bool);
608
609    fn next(&mut self) -> Option<Self::Item> {
610        let (idx_start, idx_end) = ElementType::get_attributes_idx(self.type_id);
611        let cur_pos = self.pos;
612        self.pos += 1;
613        if idx_start + cur_pos < idx_end {
614            let (name, chardata_id, required) = ATTRIBUTES[idx_start + cur_pos];
615            Some((name, &CHARACTER_DATA[chardata_id as usize], required))
616        } else {
617            None
618        }
619    }
620}
621
622/// Iterator over sub element definitions
623///
624/// returns the tuple (name: `ElementName`, etype: `ElementType`, `version_mask`: u32, `name_version_mask`: u32)
625pub struct SubelemDefinitionsIter {
626    type_id_stack: Vec<u16>,
627    indices: Vec<usize>,
628}
629
630impl Iterator for SubelemDefinitionsIter {
631    // ElementName, elementType, version_mask, is_named
632    type Item = (ElementName, ElementType, u32, u32);
633
634    fn next(&mut self) -> Option<Self::Item> {
635        if self.type_id_stack.is_empty() {
636            None
637        } else {
638            debug_assert_eq!(self.type_id_stack.len(), self.indices.len());
639
640            let depth = self.indices.len() - 1;
641            let current_type = self.type_id_stack[depth];
642            let cur_pos = self.indices[depth];
643            let (start_idx, end_idx) = ElementType::get_sub_element_idx(current_type);
644
645            if start_idx + cur_pos < end_idx {
646                match &SUBELEMENTS[start_idx + cur_pos] {
647                    SubElement::Element(idx) => {
648                        // found an element, return it and advance
649                        let name = ELEMENTS[*idx as usize].name;
650                        self.indices[depth] += 1;
651                        let ver_idx = ElementType::get_sub_element_ver(current_type);
652                        let version_mask = VERSION_INFO[ver_idx + cur_pos];
653                        let is_named = ElementType::new(*idx).short_name_version_mask().unwrap_or(0);
654                        Some((name, ElementType::new(*idx), version_mask, is_named))
655                    }
656                    SubElement::Group(groupid) => {
657                        // found a group, descend into it
658                        self.type_id_stack.push(*groupid);
659                        self.indices.push(0);
660                        self.next()
661                    }
662                }
663            } else {
664                // finished processing this element / group; remove it from the stack
665                self.indices.pop();
666                self.type_id_stack.pop();
667                if !self.indices.is_empty() {
668                    self.indices[depth - 1] += 1;
669                }
670                self.next()
671            }
672        }
673    }
674}
675
676// manually implement Debug for CharacterDataSpec; deriving it is not possible, because that fails on the check_fn field in ::Pattern.
677// The check_fn field is simply omitted here.
678impl core::fmt::Debug for CharacterDataSpec {
679    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
680        match self {
681            Self::Enum { items } => f.debug_struct("Enum").field("items", items).finish(),
682            Self::Pattern { regex, max_length, .. } => f
683                .debug_struct("Pattern")
684                .field("regex", regex)
685                .field("max_length", max_length)
686                .finish(),
687            Self::String {
688                preserve_whitespace,
689                max_length,
690            } => f
691                .debug_struct("String")
692                .field("preserve_whitespace", preserve_whitespace)
693                .field("max_length", max_length)
694                .finish(),
695            Self::UnsignedInteger => write!(f, "UnsignedInteger"),
696            Self::Float => write!(f, "Double"),
697        }
698    }
699}
700
701/// expand a version mask (u32) to a list of versions in the mask
702#[must_use]
703pub fn expand_version_mask(version_mask: u32) -> Vec<AutosarVersion> {
704    let mut versions = vec![];
705    for i in 0..u32::BITS {
706        let val = 1u32 << i;
707        if version_mask & val != 0 {
708            if let Some(enum_value) = AutosarVersion::from_val(val) {
709                versions.push(enum_value);
710            }
711        }
712    }
713
714    versions
715}
716
717pub(crate) fn hashfunc(mut data: &[u8]) -> (u32, u32, u32) {
718    const HASHCONST1: u32 = 0x541C_69B2; // these 4 constant values are not special, just random values
719    const HASHCONST2: u32 = 0x3B17_161B;
720
721    let mut f1 = 0x3314_3C63_u32;
722    let mut f2 = 0x88B0_B21E_u32;
723    while data.len() >= 4 {
724        let val = u32::from_ne_bytes(data[..4].try_into().unwrap());
725        f1 = f1.rotate_left(5).bitxor(val).wrapping_mul(HASHCONST1);
726        f2 = f2.rotate_left(6).bitxor(val).wrapping_mul(HASHCONST2);
727        data = &data[4..];
728    }
729    if data.len() >= 2 {
730        let val = u32::from(u16::from_ne_bytes(data[..2].try_into().unwrap()));
731        f1 = f1.rotate_left(5).bitxor(val).wrapping_mul(HASHCONST1);
732        f2 = f2.rotate_left(6).bitxor(val).wrapping_mul(HASHCONST2);
733        data = &data[2..];
734    }
735    if !data.is_empty() {
736        f1 = f1.rotate_left(5).bitxor(u32::from(data[0])).wrapping_mul(HASHCONST1);
737        f2 = f2.rotate_left(6).bitxor(u32::from(data[0])).wrapping_mul(HASHCONST2);
738    }
739    let g = f1.bitxor(f2);
740    (g, f1, f2)
741}
742
743#[cfg(test)]
744mod test {
745    extern crate std;
746    use alloc::string::ToString;
747    use core::str::FromStr;
748    use num_traits::FromPrimitive;
749    use std::collections::HashSet;
750
751    use super::*;
752
753    fn get_prm_char_element_type() -> ElementType {
754        let (ar_packages_type, _) = ElementType::ROOT
755            .find_sub_element(ElementName::ArPackages, u32::MAX)
756            .unwrap();
757        let (ar_package_type, _) = ar_packages_type
758            .find_sub_element(ElementName::ArPackage, u32::MAX)
759            .unwrap();
760        let (elements_type, _) = ar_package_type
761            .find_sub_element(ElementName::Elements, u32::MAX)
762            .unwrap();
763        let (documentation_type, _) = elements_type
764            .find_sub_element(ElementName::Documentation, u32::MAX)
765            .unwrap();
766        let (documentation_content_type, _) = documentation_type
767            .find_sub_element(ElementName::DocumentationContent, u32::MAX)
768            .unwrap();
769        let (prms_type, _) = documentation_content_type
770            .find_sub_element(ElementName::Prms, u32::MAX)
771            .unwrap();
772        let (prm_type, _) = prms_type.find_sub_element(ElementName::Prm, u32::MAX).unwrap();
773        let (prm_char_type, _) = prm_type.find_sub_element(ElementName::PrmChar, u32::MAX).unwrap();
774
775        prm_char_type
776    }
777
778    #[test]
779    fn find_sub_element() {
780        let prm_char_type = get_prm_char_element_type();
781        let (_, indices) = prm_char_type.find_sub_element(ElementName::Tol, 0xffffffff).unwrap();
782        assert_eq!(indices, vec![1, 0, 0, 0, 1]);
783    }
784
785    #[test]
786    fn find_sub_element_version_dependent() {
787        let (ar_packages_type, _) = ElementType::ROOT
788            .find_sub_element(ElementName::ArPackages, u32::MAX)
789            .unwrap();
790        let (ar_package_type, _) = ar_packages_type
791            .find_sub_element(ElementName::ArPackage, u32::MAX)
792            .unwrap();
793        let (elements_type, _) = ar_package_type
794            .find_sub_element(ElementName::Elements, u32::MAX)
795            .unwrap();
796        let (sw_base_type_type, _) = elements_type
797            .find_sub_element(ElementName::SwBaseType, u32::MAX)
798            .unwrap();
799        let (_, indices) = sw_base_type_type
800            .find_sub_element(ElementName::BaseTypeSize, AutosarVersion::Autosar_4_0_1 as u32)
801            .unwrap();
802        assert_eq!(indices, vec![11, 0]);
803
804        let (_, indices) = sw_base_type_type
805            .find_sub_element(ElementName::BaseTypeSize, AutosarVersion::Autosar_4_1_1 as u32)
806            .unwrap();
807        assert_eq!(indices, vec![13]);
808    }
809
810    #[test]
811    fn get_sub_element_spec() {
812        let prm_char_type = get_prm_char_element_type();
813        let (abs_type, indices) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
814        let sub_elem_spec = prm_char_type.get_sub_element_spec(&indices);
815        let (sub_element, _) = sub_elem_spec.unwrap();
816        if let SubElement::Element(idx) = sub_element {
817            let name = ELEMENTS[*idx as usize].name;
818            assert_eq!(name, ElementName::Abs);
819            assert_eq!(ElementType::new(*idx), abs_type);
820        }
821
822        // the element_indices passed to get_sub_element_spec may not be empty
823        let sub_elem_spec2 = prm_char_type.get_sub_element_spec(&[]);
824        assert!(sub_elem_spec2.is_none());
825        // element_indices is nonsense
826        let sub_elem_spec2 = prm_char_type.get_sub_element_spec(&[0, 0, 0, 0, 0, 0, 0, 0, 0]);
827        assert!(sub_elem_spec2.is_none());
828    }
829
830    #[test]
831    fn get_sub_element_version_mask() {
832        let prm_char_type = get_prm_char_element_type();
833        let (_, indices) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
834        let sub_elem_spec = prm_char_type.get_sub_element_spec(&indices).unwrap();
835        let version_mask2 = prm_char_type.get_sub_element_version_mask(&indices).unwrap();
836        let (_, version_mask) = sub_elem_spec;
837        assert_eq!(version_mask, version_mask2);
838
839        let no_result = prm_char_type.get_sub_element_version_mask(&[]);
840        assert!(no_result.is_none());
841    }
842
843    #[test]
844    fn get_sub_element_multiplicity() {
845        let prm_char_type = get_prm_char_element_type();
846        let (_, indices) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
847        let sub_elem_spec = prm_char_type.get_sub_element_spec(&indices).unwrap().0;
848        let multiplicity2 = prm_char_type.get_sub_element_multiplicity(&indices).unwrap();
849        if let SubElement::Element(idx) = sub_elem_spec {
850            let multiplicity = ELEMENTS[*idx as usize].multiplicity;
851            assert_eq!(multiplicity, multiplicity2);
852        }
853
854        let no_result = prm_char_type.get_sub_element_multiplicity(&[]);
855        assert!(no_result.is_none());
856    }
857
858    #[test]
859    fn get_sub_element_container_mode() {
860        let prm_char_type = get_prm_char_element_type();
861        let (_, indices) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
862        let mode = prm_char_type.get_sub_element_container_mode(&indices);
863        assert_eq!(mode, ContentMode::Sequence);
864    }
865
866    #[test]
867    fn find_common_group() {
868        let prm_char_type = get_prm_char_element_type();
869        let (_, indices_abs) = prm_char_type.find_sub_element(ElementName::Abs, u32::MAX).unwrap();
870        let (_, indices_tol) = prm_char_type.find_sub_element(ElementName::Tol, u32::MAX).unwrap();
871        let (_, indices_min) = prm_char_type.find_sub_element(ElementName::Min, u32::MAX).unwrap();
872        // see the documentation on find_sub_element for the complex structure under PRM-CHAR
873        // ABS and TOL share a sequence group (top level)
874        let group1 = prm_char_type.find_common_group(&indices_abs, &indices_tol);
875        assert_eq!(group1.content_mode(), ContentMode::Sequence);
876        // ABS and MIN have the second level choice group in common
877        let group2 = prm_char_type.find_common_group(&indices_abs, &indices_min);
878        assert_eq!(group2.content_mode(), ContentMode::Choice);
879    }
880
881    #[test]
882    fn find_attribute_spec() {
883        let AttributeSpec {
884            spec,
885            required,
886            version,
887        } = ElementType::ROOT.find_attribute_spec(AttributeName::xmlns).unwrap();
888        let spec_dbgstr = format!("{:#?}", spec);
889        assert!(!spec_dbgstr.is_empty());
890        // xmlns in AUTOSAR is required
891        assert!(required);
892        // must be specified both in the first and latest versions (and every one in between - not tested)
893        assert_ne!(version & AutosarVersion::Autosar_00050 as u32, 0);
894        assert_ne!(version & AutosarVersion::Autosar_4_0_1 as u32, 0);
895    }
896
897    #[test]
898    fn subelement_definition_iterator() {
899        let (ar_packages_type, _) = ElementType::ROOT
900            .find_sub_element(ElementName::ArPackages, u32::MAX)
901            .unwrap();
902        let (ar_package_type, _) = ar_packages_type
903            .find_sub_element(ElementName::ArPackage, u32::MAX)
904            .unwrap();
905        let (elements_type, _) = ar_package_type
906            .find_sub_element(ElementName::Elements, u32::MAX)
907            .unwrap();
908
909        let se_iter = elements_type.sub_element_spec_iter();
910        assert_eq!(se_iter.count(), 664); // this test breaks when support for new versions is added
911
912        let prm_char_type = get_prm_char_element_type();
913        let pc_iter = prm_char_type.sub_element_spec_iter();
914        // not all items in the sub element spec are compatible with the latest Autosar version, count only the ones that are compatible
915        let compatible_count = pc_iter
916            .filter(|(_, _, version_mask, _)| AutosarVersion::Autosar_00050.compatible(*version_mask))
917            .count();
918        assert_eq!(compatible_count, 9);
919    }
920
921    #[test]
922    fn autosar_version() {
923        // does from_str work correctly?
924        assert_eq!(
925            AutosarVersion::from_str("AUTOSAR_4-0-1.xsd").unwrap(),
926            AutosarVersion::Autosar_4_0_1
927        );
928        assert_eq!(
929            AutosarVersion::from_str("AUTOSAR_4-0-2.xsd").unwrap(),
930            AutosarVersion::Autosar_4_0_2
931        );
932        assert_eq!(
933            AutosarVersion::from_str("AUTOSAR_4-0-3.xsd").unwrap(),
934            AutosarVersion::Autosar_4_0_3
935        );
936        assert_eq!(
937            AutosarVersion::from_str("AUTOSAR_4-1-1.xsd").unwrap(),
938            AutosarVersion::Autosar_4_1_1
939        );
940        assert_eq!(
941            AutosarVersion::from_str("AUTOSAR_4-1-2.xsd").unwrap(),
942            AutosarVersion::Autosar_4_1_2
943        );
944        assert_eq!(
945            AutosarVersion::from_str("AUTOSAR_4-1-3.xsd").unwrap(),
946            AutosarVersion::Autosar_4_1_3
947        );
948        assert_eq!(
949            AutosarVersion::from_str("AUTOSAR_4-2-1.xsd").unwrap(),
950            AutosarVersion::Autosar_4_2_1
951        );
952        assert_eq!(
953            AutosarVersion::from_str("AUTOSAR_4-2-2.xsd").unwrap(),
954            AutosarVersion::Autosar_4_2_2
955        );
956        assert_eq!(
957            AutosarVersion::from_str("AUTOSAR_4-3-0.xsd").unwrap(),
958            AutosarVersion::Autosar_4_3_0
959        );
960        assert_eq!(
961            AutosarVersion::from_str("AUTOSAR_00042.xsd").unwrap(),
962            AutosarVersion::Autosar_00042
963        );
964        assert_eq!(
965            AutosarVersion::from_str("AUTOSAR_00043.xsd").unwrap(),
966            AutosarVersion::Autosar_00043
967        );
968        assert_eq!(
969            AutosarVersion::from_str("AUTOSAR_00044.xsd").unwrap(),
970            AutosarVersion::Autosar_00044
971        );
972        assert_eq!(
973            AutosarVersion::from_str("AUTOSAR_00045.xsd").unwrap(),
974            AutosarVersion::Autosar_00045
975        );
976        assert_eq!(
977            AutosarVersion::from_str("AUTOSAR_00046.xsd").unwrap(),
978            AutosarVersion::Autosar_00046
979        );
980        assert_eq!(
981            AutosarVersion::from_str("AUTOSAR_00047.xsd").unwrap(),
982            AutosarVersion::Autosar_00047
983        );
984        assert_eq!(
985            AutosarVersion::from_str("AUTOSAR_00048.xsd").unwrap(),
986            AutosarVersion::Autosar_00048
987        );
988        assert_eq!(
989            AutosarVersion::from_str("AUTOSAR_00049.xsd").unwrap(),
990            AutosarVersion::Autosar_00049
991        );
992        assert_eq!(
993            AutosarVersion::from_str("AUTOSAR_00050.xsd").unwrap(),
994            AutosarVersion::Autosar_00050
995        );
996        assert_eq!(
997            AutosarVersion::from_str("AUTOSAR_00051.xsd").unwrap(),
998            AutosarVersion::Autosar_00051
999        );
1000        assert_eq!(
1001            AutosarVersion::from_str("AUTOSAR_00052.xsd").unwrap(),
1002            AutosarVersion::Autosar_00052
1003        );
1004        assert_eq!(
1005            AutosarVersion::from_str("AUTOSAR_00053.xsd").unwrap(),
1006            AutosarVersion::Autosar_00053
1007        );
1008
1009        // do all the version descriptions exist & make sense?
1010        assert!(AutosarVersion::Autosar_4_0_1.describe().starts_with("AUTOSAR"));
1011        assert!(AutosarVersion::Autosar_4_0_2.describe().starts_with("AUTOSAR"));
1012        assert!(AutosarVersion::Autosar_4_0_3.describe().starts_with("AUTOSAR"));
1013        assert!(AutosarVersion::Autosar_4_1_1.describe().starts_with("AUTOSAR"));
1014        assert!(AutosarVersion::Autosar_4_1_2.describe().starts_with("AUTOSAR"));
1015        assert!(AutosarVersion::Autosar_4_1_3.describe().starts_with("AUTOSAR"));
1016        assert!(AutosarVersion::Autosar_4_2_1.describe().starts_with("AUTOSAR"));
1017        assert!(AutosarVersion::Autosar_4_2_2.describe().starts_with("AUTOSAR"));
1018        assert!(AutosarVersion::Autosar_4_3_0.describe().starts_with("AUTOSAR"));
1019        assert!(AutosarVersion::Autosar_00042.describe().starts_with("AUTOSAR"));
1020        assert!(AutosarVersion::Autosar_00043.describe().starts_with("AUTOSAR"));
1021        assert!(AutosarVersion::Autosar_00044.describe().starts_with("AUTOSAR"));
1022        assert!(AutosarVersion::Autosar_00045.describe().starts_with("AUTOSAR"));
1023        assert!(AutosarVersion::Autosar_00046.describe().starts_with("AUTOSAR"));
1024        assert!(AutosarVersion::Autosar_00047.describe().starts_with("AUTOSAR"));
1025        assert!(AutosarVersion::Autosar_00048.describe().starts_with("AUTOSAR"));
1026        assert!(AutosarVersion::Autosar_00049.describe().starts_with("AUTOSAR"));
1027        assert!(AutosarVersion::Autosar_00050.describe().starts_with("AUTOSAR"));
1028        assert!(AutosarVersion::Autosar_00051.describe().starts_with("AUTOSAR"));
1029        assert!(AutosarVersion::Autosar_00052.describe().starts_with("AUTOSAR"));
1030        assert!(AutosarVersion::Autosar_00053.describe().starts_with("AUTOSAR"));
1031
1032        // do all the xsd file names exist?
1033        assert!(AutosarVersion::Autosar_4_0_1.filename().ends_with(".xsd"));
1034        assert!(AutosarVersion::Autosar_4_0_2.filename().ends_with(".xsd"));
1035        assert!(AutosarVersion::Autosar_4_0_3.filename().ends_with(".xsd"));
1036        assert!(AutosarVersion::Autosar_4_1_1.filename().ends_with(".xsd"));
1037        assert!(AutosarVersion::Autosar_4_1_2.filename().ends_with(".xsd"));
1038        assert!(AutosarVersion::Autosar_4_1_3.filename().ends_with(".xsd"));
1039        assert!(AutosarVersion::Autosar_4_2_1.filename().ends_with(".xsd"));
1040        assert!(AutosarVersion::Autosar_4_2_2.filename().ends_with(".xsd"));
1041        assert!(AutosarVersion::Autosar_4_3_0.filename().ends_with(".xsd"));
1042        assert!(AutosarVersion::Autosar_00042.filename().ends_with(".xsd"));
1043        assert!(AutosarVersion::Autosar_00043.filename().ends_with(".xsd"));
1044        assert!(AutosarVersion::Autosar_00044.filename().ends_with(".xsd"));
1045        assert!(AutosarVersion::Autosar_00045.filename().ends_with(".xsd"));
1046        assert!(AutosarVersion::Autosar_00046.filename().ends_with(".xsd"));
1047        assert!(AutosarVersion::Autosar_00047.filename().ends_with(".xsd"));
1048        assert!(AutosarVersion::Autosar_00048.filename().ends_with(".xsd"));
1049        assert!(AutosarVersion::Autosar_00049.filename().ends_with(".xsd"));
1050        assert!(AutosarVersion::Autosar_00050.filename().ends_with(".xsd"));
1051        assert!(AutosarVersion::Autosar_00051.filename().ends_with(".xsd"));
1052        assert!(AutosarVersion::Autosar_00052.filename().ends_with(".xsd"));
1053        assert!(AutosarVersion::Autosar_00053.filename().ends_with(".xsd"));
1054
1055        // to_string() should give the same result as describe()
1056        assert_eq!(
1057            AutosarVersion::Autosar_4_0_1.to_string(),
1058            AutosarVersion::Autosar_4_0_1.describe()
1059        );
1060
1061        // clone impl exists
1062        let cloned = AutosarVersion::Autosar_00050;
1063        assert_eq!(cloned, AutosarVersion::Autosar_00050);
1064
1065        // version parse error
1066        let error = AutosarVersion::from_str("something else");
1067        assert_eq!(format!("{:#?}", error.unwrap_err()), "ParseAutosarVersionError");
1068
1069        //Autosar version implements Hash and can be inserted into HashSet / HashMap
1070        let mut hashset = HashSet::<AutosarVersion>::new();
1071        hashset.insert(AutosarVersion::Autosar_00050);
1072    }
1073
1074    #[test]
1075    fn attribute_name_basics() {
1076        // attribute name round trip: enum -> str -> enum
1077        assert_eq!(
1078            AttributeName::Uuid,
1079            AttributeName::from_str(AttributeName::Uuid.to_str()).unwrap()
1080        );
1081
1082        // to_string()
1083        assert_eq!(AttributeName::Uuid.to_string(), "UUID");
1084
1085        // clone impl exists
1086        let cloned = AttributeName::Uuid;
1087        assert_eq!(cloned, AttributeName::Uuid);
1088
1089        // attribute parse error
1090        let error = AttributeName::from_str("unknown attribute name");
1091        assert_eq!(format!("{:#?}", error.unwrap_err()), "ParseAttributeNameError");
1092
1093        // attribute names implement Hash and can be inserted into HashSet / HashMap
1094        let mut hashset = HashSet::<AttributeName>::new();
1095        hashset.insert(AttributeName::Dest);
1096    }
1097
1098    #[test]
1099    fn element_name_basics() {
1100        // element name round trip: enum -> str -> enum
1101        assert_eq!(
1102            ElementName::Autosar,
1103            ElementName::from_str(ElementName::Autosar.to_str()).unwrap()
1104        );
1105
1106        // to_string()
1107        assert_eq!(ElementName::Autosar.to_string(), "AUTOSAR");
1108
1109        // clone impl exists
1110        let cloned = ElementName::Autosar;
1111        assert_eq!(cloned, ElementName::Autosar);
1112
1113        // element name parse error
1114        let error = ElementName::from_str("unknown element name");
1115        assert_eq!(format!("{:#?}", error.unwrap_err()), "ParseElementNameError");
1116
1117        // element names implement Hash and can be inserted into HashSet / HashMap
1118        let mut hashset = HashSet::<ElementName>::new();
1119        hashset.insert(ElementName::Autosar);
1120    }
1121
1122    #[test]
1123    fn enum_item_basics() {
1124        // enum item round trip: enum -> str -> enum
1125        assert_eq!(
1126            EnumItem::Default,
1127            EnumItem::from_str(EnumItem::Default.to_str()).unwrap()
1128        );
1129
1130        // to_string()
1131        assert_eq!(EnumItem::Default.to_string(), "DEFAULT");
1132
1133        // clone impl exists
1134        let cloned = EnumItem::Abstract;
1135        assert_eq!(cloned, EnumItem::Abstract);
1136
1137        // enum item parse error
1138        let error = EnumItem::from_str("unknown enum item");
1139        assert_eq!(format!("{:#?}", error.unwrap_err()), "ParseEnumItemError");
1140
1141        // enum items implement Hash and can be inserted into HashSet / HashMap
1142        let mut hashset = HashSet::<EnumItem>::new();
1143        hashset.insert(EnumItem::Abstract);
1144    }
1145
1146    #[test]
1147    fn ordered() {
1148        let (ar_packages_type, _) = ElementType::ROOT
1149            .find_sub_element(ElementName::ArPackages, u32::MAX)
1150            .unwrap();
1151        let (ar_package_type, _) = ar_packages_type
1152            .find_sub_element(ElementName::ArPackage, u32::MAX)
1153            .unwrap();
1154        let (elements_type, _) = ar_package_type
1155            .find_sub_element(ElementName::Elements, u32::MAX)
1156            .unwrap();
1157        // BSW-MODULE-ENTRY: This class represents a single API entry (C-function prototype) into the BSW module or cluster.
1158        let (bsw_module_entry, _) = elements_type
1159            .find_sub_element(ElementName::BswModuleEntry, u32::MAX)
1160            .unwrap();
1161        // ARGUMENTS: Arguments belonging of the BswModuleEntry.
1162        let (arguments, _) = bsw_module_entry
1163            .find_sub_element(ElementName::Arguments, u32::MAX)
1164            .unwrap();
1165
1166        assert!(!bsw_module_entry.is_ordered());
1167        assert!(arguments.is_ordered());
1168    }
1169
1170    #[test]
1171    fn splittable() {
1172        let (ar_packages_type, _) = ElementType::ROOT
1173            .find_sub_element(ElementName::ArPackages, u32::MAX)
1174            .unwrap();
1175        let (ar_package_type, _) = ar_packages_type
1176            .find_sub_element(ElementName::ArPackage, u32::MAX)
1177            .unwrap();
1178        let (elements_type, _) = ar_package_type
1179            .find_sub_element(ElementName::Elements, u32::MAX)
1180            .unwrap();
1181
1182        assert!(!ar_package_type.splittable_in(AutosarVersion::Autosar_00051));
1183        assert_ne!(ar_packages_type.splittable() & AutosarVersion::Autosar_00051 as u32, 0);
1184        assert!(ar_packages_type.splittable_in(AutosarVersion::Autosar_00051));
1185        assert_ne!(elements_type.splittable() & AutosarVersion::Autosar_00051 as u32, 0);
1186    }
1187
1188    #[test]
1189    fn std_restriction() {
1190        let (ar_packages_type, _) = ElementType::ROOT
1191            .find_sub_element(ElementName::ArPackages, u32::MAX)
1192            .unwrap();
1193        let (ar_package_type, _) = ar_packages_type
1194            .find_sub_element(ElementName::ArPackage, u32::MAX)
1195            .unwrap();
1196        let (elements_type, _) = ar_package_type
1197            .find_sub_element(ElementName::Elements, u32::MAX)
1198            .unwrap();
1199        let (machine_type, _) = elements_type.find_sub_element(ElementName::Machine, u32::MAX).unwrap();
1200        let (defapp_timeout_type, _) = machine_type
1201            .find_sub_element(ElementName::DefaultApplicationTimeout, u32::MAX)
1202            .unwrap();
1203
1204        assert_eq!(ar_package_type.std_restriction(), StdRestrict::NotRestricted);
1205        assert_eq!(defapp_timeout_type.std_restriction(), StdRestrict::AdaptivePlatform);
1206    }
1207
1208    #[test]
1209    fn reference_dest() {
1210        let (ar_packages_type, _) = ElementType::ROOT
1211            .find_sub_element(ElementName::ArPackages, u32::MAX)
1212            .unwrap();
1213        let (ar_package_type, _) = ar_packages_type
1214            .find_sub_element(ElementName::ArPackage, u32::MAX)
1215            .unwrap();
1216        let (elements_type, _) = ar_package_type
1217            .find_sub_element(ElementName::Elements, u32::MAX)
1218            .unwrap();
1219        let (can_tp_config_type, _) = elements_type
1220            .find_sub_element(ElementName::CanTpConfig, u32::MAX)
1221            .unwrap();
1222        let (tp_connections_type, _) = can_tp_config_type
1223            .find_sub_element(ElementName::TpConnections, u32::MAX)
1224            .unwrap();
1225        let (can_tp_connection_type, _) = tp_connections_type
1226            .find_sub_element(ElementName::CanTpConnection, u32::MAX)
1227            .unwrap();
1228        let (ident_type, _) = can_tp_connection_type
1229            .find_sub_element(ElementName::Ident, u32::MAX)
1230            .unwrap();
1231
1232        let (diagnostic_connection_type, _) = elements_type
1233            .find_sub_element(ElementName::DiagnosticConnection, u32::MAX)
1234            .unwrap();
1235        let (physical_request_ref_type, _) = diagnostic_connection_type
1236            .find_sub_element(ElementName::PhysicalRequestRef, u32::MAX)
1237            .unwrap();
1238
1239        let ref_value = physical_request_ref_type.reference_dest_value(&ident_type).unwrap();
1240        assert_eq!(ref_value, EnumItem::TpConnectionIdent);
1241        assert!(ident_type.verify_reference_dest(ref_value));
1242        let invalid_ref = physical_request_ref_type.reference_dest_value(&tp_connections_type);
1243        assert!(invalid_ref.is_none());
1244    }
1245
1246    #[test]
1247    fn traits() {
1248        // this test is basically nonsense - derived traits should all be ok
1249        // but there is no way to exclude them from coverage
1250        // ElementMultiplicity: Debug & Clone
1251        let mult = ElementMultiplicity::Any;
1252        let m2 = mult; // must be .clone(), otherwise the copy impl is tested instead
1253        assert_eq!(format!("{:#?}", mult), format!("{:#?}", m2));
1254
1255        // ContentMode: Debug, Clone
1256        let cm = ContentMode::Sequence;
1257        let cm2 = cm; // must be .clone(), otherwise the copy impl is tested instead
1258        assert_eq!(format!("{:#?}", cm), format!("{:#?}", cm2));
1259
1260        // ElementType: Debug, Clone, Eq & Hash
1261        let et = ElementType::ROOT;
1262        let et2 = et; // must be .clone(), otherwise the copy impl is tested instead
1263        assert_eq!(format!("{:#?}", et), format!("{:#?}", et2));
1264        let mut hashset = HashSet::<ElementType>::new();
1265        hashset.insert(et);
1266        let inserted = hashset.insert(et2);
1267        assert!(!inserted);
1268
1269        // AutosarVersion: Debug, Clone, Hash
1270        let ver = AutosarVersion::LATEST;
1271        let ver2 = ver; // must be .clone(), otherwise the copy impl is tested instead
1272        assert_eq!(format!("{ver:#?}"), format!("{ver2:#?}"));
1273        let mut hashset = HashSet::<AutosarVersion>::new();
1274        hashset.insert(ver);
1275        let inserted = hashset.insert(ver2);
1276        assert!(!inserted);
1277
1278        // ElementName: Debug, Clone, Hash
1279        let en = ElementName::Autosar;
1280        let en2 = en; // must be .clone(), otherwise the copy impl is tested instead
1281        assert_eq!(format!("{en:#?}"), format!("{en2:#?}"));
1282        let mut hashset = HashSet::<ElementName>::new();
1283        hashset.insert(en);
1284        let inserted = hashset.insert(en2);
1285        assert!(!inserted);
1286
1287        // CharacterDataSpec: Debug
1288        let cdata_spec = CharacterDataSpec::String {
1289            preserve_whitespace: true,
1290            max_length: None,
1291        };
1292        let txt = format!("{cdata_spec:#?}");
1293        assert!(txt.starts_with("String"));
1294        let cdata_spec = CharacterDataSpec::Pattern {
1295            check_fn: crate::regex::validate_regex_1,
1296            regex: r"0x[0-9a-z]*",
1297            max_length: None,
1298        };
1299        let txt = format!("{cdata_spec:#?}");
1300        assert!(txt.starts_with("Pattern"));
1301        let cdata_spec = CharacterDataSpec::Enum {
1302            items: &[(EnumItem::Custom, 0x7e000)],
1303        };
1304        let txt = format!("{cdata_spec:#?}");
1305        assert!(txt.starts_with("Enum"));
1306        let cdata_spec = CharacterDataSpec::Float;
1307        let txt = format!("{cdata_spec:#?}");
1308        assert!(txt.starts_with("Double"));
1309        let cdata_spec = CharacterDataSpec::UnsignedInteger;
1310        let txt = format!("{cdata_spec:#?}");
1311        assert!(txt.starts_with("UnsignedInteger"));
1312    }
1313
1314    #[test]
1315    fn test_expand_version_mask() {
1316        let (ar_packages_type, _) = ElementType::ROOT
1317            .find_sub_element(ElementName::ArPackages, u32::MAX)
1318            .unwrap();
1319        let (ar_package_type, _) = ar_packages_type
1320            .find_sub_element(ElementName::ArPackage, u32::MAX)
1321            .unwrap();
1322        let (elements_type, _) = ar_package_type
1323            .find_sub_element(ElementName::Elements, u32::MAX)
1324            .unwrap();
1325        let (_, element_indices) = elements_type
1326            .find_sub_element(ElementName::AdaptiveApplicationSwComponentType, u32::MAX)
1327            .unwrap();
1328        let version_mask = elements_type.get_sub_element_version_mask(&element_indices).unwrap();
1329
1330        assert_eq!(
1331            &[
1332                AutosarVersion::Autosar_00042,
1333                AutosarVersion::Autosar_00043,
1334                AutosarVersion::Autosar_00044,
1335                AutosarVersion::Autosar_00045,
1336                AutosarVersion::Autosar_00046,
1337                AutosarVersion::Autosar_00047,
1338                AutosarVersion::Autosar_00048,
1339                AutosarVersion::Autosar_00049,
1340                AutosarVersion::Autosar_00050,
1341                AutosarVersion::Autosar_00051,
1342                AutosarVersion::Autosar_00052,
1343                AutosarVersion::Autosar_00053,
1344            ],
1345            &*expand_version_mask(version_mask)
1346        );
1347    }
1348
1349    #[test]
1350    fn test_version_masks() {
1351        assert_eq!(AutosarVersion::from_u64(0x1), Some(AutosarVersion::Autosar_4_0_1));
1352        assert_eq!(AutosarVersion::from_u64(0x2), Some(AutosarVersion::Autosar_4_0_2));
1353        assert_eq!(AutosarVersion::from_u64(0x4), Some(AutosarVersion::Autosar_4_0_3));
1354        assert_eq!(AutosarVersion::from_u64(0x8), Some(AutosarVersion::Autosar_4_1_1));
1355        assert_eq!(AutosarVersion::from_u64(0x10), Some(AutosarVersion::Autosar_4_1_2));
1356        assert_eq!(AutosarVersion::from_u64(0x20), Some(AutosarVersion::Autosar_4_1_3));
1357        assert_eq!(AutosarVersion::from_u64(0x40), Some(AutosarVersion::Autosar_4_2_1));
1358        assert_eq!(AutosarVersion::from_u64(0x80), Some(AutosarVersion::Autosar_4_2_2));
1359        assert_eq!(AutosarVersion::from_u64(0x100), Some(AutosarVersion::Autosar_4_3_0));
1360        assert_eq!(AutosarVersion::from_u64(0x200), Some(AutosarVersion::Autosar_00042));
1361        assert_eq!(AutosarVersion::from_u64(0x400), Some(AutosarVersion::Autosar_00043));
1362        assert_eq!(AutosarVersion::from_u64(0x800), Some(AutosarVersion::Autosar_00044));
1363        assert_eq!(AutosarVersion::from_u64(0x1000), Some(AutosarVersion::Autosar_00045));
1364        assert_eq!(AutosarVersion::from_u64(0x2000), Some(AutosarVersion::Autosar_00046));
1365        assert_eq!(AutosarVersion::from_u64(0x4000), Some(AutosarVersion::Autosar_00047));
1366        assert_eq!(AutosarVersion::from_u64(0x8000), Some(AutosarVersion::Autosar_00048));
1367        assert_eq!(AutosarVersion::from_u64(0x10000), Some(AutosarVersion::Autosar_00049));
1368        assert_eq!(AutosarVersion::from_u64(0x20000), Some(AutosarVersion::Autosar_00050));
1369        assert_eq!(AutosarVersion::from_u64(0x40000), Some(AutosarVersion::Autosar_00051));
1370        assert_eq!(AutosarVersion::from_u64(0x80000), Some(AutosarVersion::Autosar_00052));
1371        assert_eq!(AutosarVersion::from_u64(0x100000), Some(AutosarVersion::Autosar_00053));
1372        // invalid version mask: more than one bit set
1373        assert_eq!(AutosarVersion::from_u64(0xF), None);
1374
1375        // FromPrimitive also provides from_i64
1376        assert_eq!(AutosarVersion::from_i64(0x1), Some(AutosarVersion::Autosar_4_0_1));
1377        assert_eq!(AutosarVersion::from_i64(-1), None);
1378    }
1379
1380    #[cfg(feature = "docstrings")]
1381    #[test]
1382    fn test_docstring() {
1383        let (ar_packages_type, _) = ElementType::ROOT
1384            .find_sub_element(ElementName::ArPackages, u32::MAX)
1385            .unwrap();
1386        let docstring = ar_packages_type.docstring();
1387        assert_eq!(docstring, "This is the top level package in an AUTOSAR model.");
1388    }
1389}