Skip to main content

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