autosar_data_abstraction/ecu_configuration/definition/
mod.rs

1use crate::{
2    AbstractionElement, ArPackage, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element,
3};
4use autosar_data::{Element, ElementName, EnumItem};
5
6mod container;
7mod parameter;
8mod reference;
9
10pub use container::*;
11pub use parameter::*;
12pub use reference::*;
13
14//#########################################################
15
16/// `EcucCommonAttributes` provides methods to modify attributes that are shared by all parameters and references
17pub trait EcucCommonAttributes: EcucDefinitionElement {
18    /// set the multiplicity config classes of the parameter definition.
19    /// If an empty list is provided, the multiplicity config classes are removed.
20    ///
21    /// This setting is required if the containing `EcucModuleDef` has the category `VENDOR_SPECIFIC_MODULE_DEFINITION`.
22    fn set_multiplicity_config_classes(
23        &self,
24        config: &[(EcucConfigurationClass, EcucConfigurationVariant)],
25    ) -> Result<(), AutosarAbstractionError> {
26        set_config_classes(
27            self.element(),
28            ElementName::MultiplicityConfigClasses,
29            ElementName::EcucMultiplicityConfigurationClass,
30            config,
31        )?;
32        Ok(())
33    }
34
35    /// get the multiplicity config classes of the parameter definition
36    #[must_use]
37    fn multiplicity_config_classes(&self) -> Vec<(EcucConfigurationClass, EcucConfigurationVariant)> {
38        get_config_classes(self.element(), ElementName::MultiplicityConfigClasses)
39    }
40
41    /// set the origin of the parameter definition
42    ///
43    /// The origin is a string that describes if the parameter was defined in the AUTOSAR standard or by a vendor.
44    /// Standardized parameters use the origin "AUTOSAR_ECUC", while vendors are supposed to use string like "VendorXyz_v1.3"
45    fn set_origin(&self, origin: &str) -> Result<(), AutosarAbstractionError> {
46        self.element()
47            .get_or_create_sub_element(ElementName::Origin)?
48            .set_character_data(origin)?;
49        Ok(())
50    }
51
52    /// get the origin of the parameter definition
53    ///
54    /// The origin is a string that describes if the parameter was defined in the AUTOSAR standard or by a vendor.
55    /// Standardized parameters use the origin "AUTOSAR_ECUC", while vendors are supposed to use string like "VendorXyz_v1.3"
56    #[must_use]
57    fn origin(&self) -> Option<String> {
58        self.element()
59            .get_sub_element(ElementName::Origin)?
60            .character_data()?
61            .string_value()
62    }
63
64    /// set or remove the postBuildVariantMultiplicity attribute
65    ///
66    /// If postBuildVariantMultiplicity is true, then the parameter or reference
67    /// may have a different number of instances in different post-build variants.
68    fn set_post_build_variant_multiplicity(
69        &self,
70        post_build_variant_multiplicity: Option<bool>,
71    ) -> Result<(), AutosarAbstractionError> {
72        if let Some(post_build_variant_multiplicity) = post_build_variant_multiplicity {
73            self.element()
74                .get_or_create_sub_element(ElementName::PostBuildVariantMultiplicity)?
75                .set_character_data(post_build_variant_multiplicity)?;
76        } else {
77            let _ = self
78                .element()
79                .remove_sub_element_kind(ElementName::PostBuildVariantMultiplicity);
80        }
81
82        Ok(())
83    }
84
85    /// get the postBuildVariantMultiplicity attribute
86    ///
87    /// If postBuildVariantMultiplicity is true, then the parameter or reference
88    /// may have a different number of instances in different post-build variants.
89    #[must_use]
90    fn post_build_variant_multiplicity(&self) -> Option<bool> {
91        self.element()
92            .get_sub_element(ElementName::PostBuildVariantMultiplicity)?
93            .character_data()?
94            .parse_bool()
95    }
96
97    /// set or remove the postBuildVariantValue attribute
98    ///
99    /// If postBuildVariantValue is true, then the parameter or reference
100    /// may have different values in different post-build variants.
101    fn set_post_build_variant_value(
102        &self,
103        post_build_variant_value: Option<bool>,
104    ) -> Result<(), AutosarAbstractionError> {
105        if let Some(post_build_variant_value) = post_build_variant_value {
106            self.element()
107                .get_or_create_sub_element(ElementName::PostBuildVariantValue)?
108                .set_character_data(post_build_variant_value)?;
109        } else {
110            let _ = self
111                .element()
112                .remove_sub_element_kind(ElementName::PostBuildVariantValue);
113        }
114
115        Ok(())
116    }
117
118    /// get the postBuildVariantValue attribute
119    ///
120    /// If postBuildVariantValue is true, then the parameter or reference
121    /// may have different values in different post-build variants.
122    #[must_use]
123    fn post_build_variant_value(&self) -> Option<bool> {
124        self.element()
125            .get_sub_element(ElementName::PostBuildVariantValue)?
126            .character_data()?
127            .parse_bool()
128    }
129
130    /// set or remove the requiresIndex attribute
131    fn set_requires_index(&self, requires_index: Option<bool>) -> Result<(), AutosarAbstractionError> {
132        if let Some(requires_index) = requires_index {
133            self.element()
134                .get_or_create_sub_element(ElementName::RequiresIndex)?
135                .set_character_data(requires_index)?;
136        } else {
137            let _ = self.element().remove_sub_element_kind(ElementName::RequiresIndex);
138        }
139
140        Ok(())
141    }
142
143    /// get the requiresIndex attribute
144    #[must_use]
145    fn requires_index(&self) -> Option<bool> {
146        self.element()
147            .get_sub_element(ElementName::RequiresIndex)?
148            .character_data()?
149            .parse_bool()
150    }
151
152    /// set the value config classes of the parameter definition.
153    ///
154    /// If an empty list is provided, the value config classes are removed.
155    /// According to the specification setting is required if the containing `EcucModuleDef`
156    /// has the category "VENDOR_SPECIFIC_MODULE_DEFINITION", but in practice it is rarely used.
157    fn set_value_config_classes(
158        &self,
159        config: &[(EcucConfigurationClass, EcucConfigurationVariant)],
160    ) -> Result<(), AutosarAbstractionError> {
161        set_config_classes(
162            self.element(),
163            ElementName::ValueConfigClasses,
164            ElementName::EcucValueConfigurationClass,
165            config,
166        )?;
167        Ok(())
168    }
169
170    /// get the value config classes of the parameter definition
171    ///
172    /// According to the specification setting is required if the containing `EcucModuleDef`
173    /// has the category "VENDOR_SPECIFIC_MODULE_DEFINITION", but in practice it is rarely used.
174    #[must_use]
175    fn value_config_classes(&self) -> Vec<(EcucConfigurationClass, EcucConfigurationVariant)> {
176        get_config_classes(self.element(), ElementName::ValueConfigClasses)
177    }
178
179    /// set or remove the withAuto attribute
180    ///
181    /// If withAuto is true, then the parameter or reference is allowed to set its isAutoValue attribute to true.
182    fn set_with_auto(&self, with_auto: Option<bool>) -> Result<(), AutosarAbstractionError> {
183        if let Some(with_auto) = with_auto {
184            self.element()
185                .get_or_create_sub_element(ElementName::WithAuto)?
186                .set_character_data(with_auto)?;
187        } else {
188            let _ = self.element().remove_sub_element_kind(ElementName::WithAuto);
189        }
190
191        Ok(())
192    }
193
194    /// get the withAuto attribute
195    fn with_auto(&self) -> Option<bool> {
196        self.element()
197            .get_sub_element(ElementName::WithAuto)?
198            .character_data()?
199            .parse_bool()
200    }
201}
202
203/// `EcucDefinitionElement` provides methods to modify attributes that are shared by all elements of the ecuc definition
204pub trait EcucDefinitionElement: AbstractionElement {
205    /// set or remove the lower multiplicity attribute
206    fn set_lower_multiplicity(&self, lower_multiplicity: Option<u32>) -> Result<(), AutosarAbstractionError> {
207        if let Some(lower_multiplicity) = lower_multiplicity {
208            self.element()
209                .get_or_create_sub_element(ElementName::LowerMultiplicity)?
210                .set_character_data(lower_multiplicity as u64)?;
211        } else {
212            let _ = self.element().remove_sub_element_kind(ElementName::LowerMultiplicity);
213        }
214
215        Ok(())
216    }
217
218    /// get the lower multiplicity attribute
219    #[must_use]
220    fn lower_multiplicity(&self) -> Option<u32> {
221        self.element()
222            .get_sub_element(ElementName::LowerMultiplicity)
223            .and_then(|elem| elem.character_data())
224            .and_then(|cdata| cdata.parse_integer())
225    }
226
227    /// set or remove the upper multiplicity attribute
228    fn set_upper_multiplicity(&self, upper_multiplicity: Option<u32>) -> Result<(), AutosarAbstractionError> {
229        if let Some(upper_multiplicity) = upper_multiplicity {
230            self.element()
231                .get_or_create_sub_element(ElementName::UpperMultiplicity)?
232                .set_character_data(upper_multiplicity as u64)?;
233        } else {
234            let _ = self.element().remove_sub_element_kind(ElementName::UpperMultiplicity);
235        }
236
237        Ok(())
238    }
239
240    /// get the upper multiplicity attribute
241    #[must_use]
242    fn upper_multiplicity(&self) -> Option<u32> {
243        self.element()
244            .get_sub_element(ElementName::UpperMultiplicity)
245            .and_then(|elem| elem.character_data())
246            .and_then(|cdata| cdata.parse_integer())
247    }
248
249    /// set or remove the upper multiplicity infinite attribute
250    ///
251    /// if this attribute is set to true, the upper multiplicity is infinite
252    /// (i.e. the module definition can be used an arbitrary number of times)
253    /// When this attribute is true, the upper multiplicity attribute automatically removed.
254    fn set_upper_multiplicity_infinite(&self, infinite: Option<bool>) -> Result<(), AutosarAbstractionError> {
255        if let Some(infinite) = infinite {
256            self.element()
257                .get_or_create_sub_element(ElementName::UpperMultiplicityInfinite)?
258                .set_character_data(infinite)?;
259            if infinite {
260                let _ = self.element().remove_sub_element_kind(ElementName::UpperMultiplicity);
261            }
262        } else {
263            let _ = self
264                .element()
265                .remove_sub_element_kind(ElementName::UpperMultiplicityInfinite);
266        }
267
268        Ok(())
269    }
270
271    /// get the upper multiplicity infinite attribute
272    #[must_use]
273    fn upper_multiplicity_infinite(&self) -> Option<bool> {
274        self.element()
275            .get_sub_element(ElementName::UpperMultiplicityInfinite)
276            .and_then(|elem| elem.character_data())
277            .and_then(|cdata| cdata.parse_bool())
278    }
279}
280
281//#########################################################
282
283/// The `EcucDefinitionCollection` is a container for all module definitions in the ECU configuration
284#[derive(Debug, Clone, PartialEq, Eq, Hash)]
285pub struct EcucDefinitionCollection(Element);
286abstraction_element!(EcucDefinitionCollection, EcucDefinitionCollection);
287impl IdentifiableAbstractionElement for EcucDefinitionCollection {}
288
289impl EcucDefinitionCollection {
290    pub(crate) fn new(name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
291        let elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
292        let ecuc_definition_collection_elem =
293            elements.create_named_sub_element(ElementName::EcucDefinitionCollection, name)?;
294
295        Ok(Self(ecuc_definition_collection_elem))
296    }
297
298    /// add a reference to a module definition to the collection
299    pub fn add_module_def(&self, module_def: &EcucModuleDef) -> Result<(), AutosarAbstractionError> {
300        self.element()
301            .get_or_create_sub_element(ElementName::ModuleRefs)?
302            .create_sub_element(ElementName::ModuleRef)?
303            .set_reference_target(module_def.element())?;
304        Ok(())
305    }
306
307    /// iterate over all module definitions in the collection
308    pub fn module_defs(&self) -> impl Iterator<Item = EcucModuleDef> + Send + use<> {
309        self.element()
310            .get_sub_element(ElementName::ModuleRefs)
311            .into_iter()
312            .flat_map(|module_refs_elem| module_refs_elem.sub_elements())
313            .filter_map(|module_ref_elem| module_ref_elem.get_reference_target().ok())
314            .filter_map(|module_def_elem| EcucModuleDef::try_from(module_def_elem).ok())
315    }
316}
317
318//#########################################################
319
320/// The `EcucModuleDef` is a container for the definition of a single base software module
321#[derive(Debug, Clone, PartialEq, Eq, Hash)]
322pub struct EcucModuleDef(Element);
323abstraction_element!(EcucModuleDef, EcucModuleDef);
324impl IdentifiableAbstractionElement for EcucModuleDef {}
325impl EcucDefinitionElement for EcucModuleDef {}
326
327impl EcucModuleDef {
328    pub(crate) fn new(name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
329        let elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
330        let ecuc_module_def_elem = elements.create_named_sub_element(ElementName::EcucModuleDef, name)?;
331
332        Ok(Self(ecuc_module_def_elem))
333    }
334
335    /// create a new `EcucChoiceContainerDef` in the module
336    pub fn create_choice_container_def(&self, name: &str) -> Result<EcucChoiceContainerDef, AutosarAbstractionError> {
337        let containers_elem = self.element().get_or_create_sub_element(ElementName::Containers)?;
338        EcucChoiceContainerDef::new(name, &containers_elem)
339    }
340
341    /// create a new `EcucParamConfContainerDef` in the module
342    pub fn create_param_conf_container_def(
343        &self,
344        name: &str,
345    ) -> Result<EcucParamConfContainerDef, AutosarAbstractionError> {
346        let containers_elem = self.element().get_or_create_sub_element(ElementName::Containers)?;
347        EcucParamConfContainerDef::new(name, &containers_elem)
348    }
349
350    /// iterate over all containers in the module
351    pub fn containers(&self) -> impl Iterator<Item = EcucContainerDef> + Send + use<> {
352        self.element()
353            .get_sub_element(ElementName::Containers)
354            .into_iter()
355            .flat_map(|containers_elem| containers_elem.sub_elements())
356            .filter_map(|container_elem| EcucContainerDef::try_from(container_elem).ok())
357    }
358
359    /// set or remove the apiServicePrefix for the module
360    ///
361    /// for CDD modules the short name of the module is always "CDD", so
362    /// this attribute is needed to define the prefix for the API services
363    pub fn set_api_service_prefix(&self, prefix: Option<&str>) -> Result<(), AutosarAbstractionError> {
364        if let Some(prefix) = prefix {
365            self.element()
366                .get_or_create_sub_element(ElementName::ApiServicePrefix)?
367                .set_character_data(prefix)?;
368        } else {
369            let _ = self.element().remove_sub_element_kind(ElementName::ApiServicePrefix);
370        }
371
372        Ok(())
373    }
374
375    /// get the apiServicePrefix for the module
376    ///
377    /// for CDD modules the short name of the module is always "CDD", so
378    /// this attribute is needed to define the prefix for the API services
379    #[must_use]
380    pub fn api_service_prefix(&self) -> Option<String> {
381        self.element()
382            .get_sub_element(ElementName::ApiServicePrefix)
383            .and_then(|elem| elem.character_data())
384            .and_then(|cdata| cdata.string_value())
385    }
386
387    /// set the supported configuration variants for the module
388    pub fn set_supported_config_variants(
389        &self,
390        variants: &[EcucConfigurationVariant],
391    ) -> Result<(), AutosarAbstractionError> {
392        // remove the old supported configuration variants list
393        let _ = self
394            .element()
395            .remove_sub_element_kind(ElementName::SupportedConfigVariants);
396
397        // create the new supported configuration variants list
398        let supported_config_variants_elem = self
399            .element()
400            .create_sub_element(ElementName::SupportedConfigVariants)?;
401        for variant in variants {
402            let variant_elem =
403                supported_config_variants_elem.create_sub_element(ElementName::SupportedConfigVariant)?;
404            variant_elem.set_character_data::<EnumItem>((*variant).into())?;
405        }
406
407        Ok(())
408    }
409
410    /// get the supported configuration variants for the module
411    #[must_use]
412    pub fn supported_config_variants(&self) -> Vec<EcucConfigurationVariant> {
413        self.element()
414            .get_sub_element(ElementName::SupportedConfigVariants)
415            .map(|elem| {
416                elem.sub_elements()
417                    .filter_map(|variant_elem| {
418                        variant_elem
419                            .character_data()
420                            .and_then(|cdata| cdata.enum_value())
421                            .and_then(|enum_item| EcucConfigurationVariant::try_from(enum_item).ok())
422                    })
423                    .collect()
424            })
425            .unwrap_or_default()
426    }
427
428    /// set or remove the post build variant support attribute
429    pub fn set_post_build_variant_support(&self, support: Option<bool>) -> Result<(), AutosarAbstractionError> {
430        if let Some(support) = support {
431            self.element()
432                .get_or_create_sub_element(ElementName::PostBuildVariantSupport)?
433                .set_character_data(support)?;
434        } else {
435            let _ = self
436                .element()
437                .remove_sub_element_kind(ElementName::PostBuildVariantSupport);
438        }
439
440        Ok(())
441    }
442
443    /// get the post build variant support attribute
444    #[must_use]
445    pub fn post_build_variant_support(&self) -> Option<bool> {
446        self.element()
447            .get_sub_element(ElementName::PostBuildVariantSupport)
448            .and_then(|elem| elem.character_data())
449            .and_then(|cdata| cdata.parse_bool())
450    }
451
452    /// set or remove the category of the module definition
453    pub fn set_category(&self, category: Option<EcucModuleDefCategory>) -> Result<(), AutosarAbstractionError> {
454        if let Some(category) = category {
455            self.element()
456                .get_or_create_sub_element(ElementName::Category)?
457                .set_character_data(category.to_string())?;
458        } else {
459            let _ = self.element().remove_sub_element_kind(ElementName::Category);
460        }
461
462        Ok(())
463    }
464
465    /// get the category of the module definition
466    #[must_use]
467    pub fn category(&self) -> Option<EcucModuleDefCategory> {
468        self.element()
469            .get_sub_element(ElementName::Category)
470            .and_then(|elem| elem.character_data())
471            .and_then(|cdata| cdata.string_value())
472            .and_then(|value| EcucModuleDefCategory::try_from(value.as_str()).ok())
473    }
474
475    /// set or remove the reference to a refined standard module
476    ///
477    /// This reference is only used if the category is `VendorSpecificModuleDefinition`
478    pub fn set_refined_module_def(
479        &self,
480        refined_module_def: Option<&EcucModuleDef>,
481    ) -> Result<(), AutosarAbstractionError> {
482        if let Some(refined_module_def) = refined_module_def {
483            self.element()
484                .get_or_create_sub_element(ElementName::RefinedModuleDefRef)?
485                .set_reference_target(refined_module_def.element())?;
486        } else {
487            let _ = self.element().remove_sub_element_kind(ElementName::RefinedModuleDefRef);
488        }
489
490        Ok(())
491    }
492
493    /// get the reference to a refined standard module
494    ///
495    /// This reference is only used if the category is `VendorSpecificModuleDefinition`
496    #[must_use]
497    pub fn refined_module_def(&self) -> Option<EcucModuleDef> {
498        self.element()
499            .get_sub_element(ElementName::RefinedModuleDefRef)
500            .and_then(|elem| elem.get_reference_target().ok())
501            .and_then(|target| EcucModuleDef::try_from(target).ok())
502    }
503}
504
505//#########################################################
506
507/// `EcucConfigurationVariant` provides the different configuration variants that
508/// can be used by the module definition.
509#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
510pub enum EcucConfigurationVariant {
511    /// Preconfigured (i.e. fixed) configuration which cannot be changed.
512    PreconfiguredConfiguration,
513    /// Recommended configuration
514    RecommendedConfiguration,
515    /// the BSW Module implementation may use `PreCompileTime` and `LinkTime` configuration parameters
516    VariantLinkTime,
517    /// the BSW Module implementation may use `PreCompileTime`, `LinkTime` and `PostBuild` configuration parameters
518    VariantPostBuild,
519    /// the BSW Module implementation may use `PreCompileTime` configuration parameters
520    VariantPreCompile,
521    /// deprecated in Autosar 4.2.1 - the BSW Module implementation may use `PreCompileTime`, `LinkTime` and `PostBuild` loadable configuration parameters
522    VariantPostBuildLoadable,
523    /// deprecated in Autosar 4.2.1 - the BSW Module implementation may use `PreCompileTime`, `LinkTime` and `PostBuild` selectable configuration parameters
524    VariantPostBuildSelectable,
525}
526
527impl From<EcucConfigurationVariant> for EnumItem {
528    fn from(value: EcucConfigurationVariant) -> Self {
529        match value {
530            EcucConfigurationVariant::PreconfiguredConfiguration => Self::PreconfiguredConfiguration,
531            EcucConfigurationVariant::RecommendedConfiguration => Self::RecommendedConfiguration,
532            EcucConfigurationVariant::VariantLinkTime => Self::VariantLinkTime,
533            EcucConfigurationVariant::VariantPostBuild => Self::VariantPostBuild,
534            EcucConfigurationVariant::VariantPreCompile => Self::VariantPreCompile,
535            EcucConfigurationVariant::VariantPostBuildLoadable => Self::VariantPostBuildLoadable,
536            EcucConfigurationVariant::VariantPostBuildSelectable => Self::VariantPostBuildSelectable,
537        }
538    }
539}
540
541impl TryFrom<EnumItem> for EcucConfigurationVariant {
542    type Error = AutosarAbstractionError;
543
544    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
545        match value {
546            EnumItem::PreconfiguredConfiguration => Ok(Self::PreconfiguredConfiguration),
547            EnumItem::RecommendedConfiguration => Ok(Self::RecommendedConfiguration),
548            EnumItem::VariantLinkTime => Ok(Self::VariantLinkTime),
549            EnumItem::VariantPostBuild => Ok(Self::VariantPostBuild),
550            EnumItem::VariantPreCompile => Ok(Self::VariantPreCompile),
551            EnumItem::VariantPostBuildLoadable => Ok(Self::VariantPostBuildLoadable),
552            EnumItem::VariantPostBuildSelectable => Ok(Self::VariantPostBuildSelectable),
553            _ => Err(AutosarAbstractionError::ValueConversionError {
554                value: value.to_string(),
555                dest: "EcucConfigurationVariant".to_string(),
556            }),
557        }
558    }
559}
560
561//#########################################################
562
563/// `EcucConfigurationClassEnum` provides the different configuration classes for Autosar configuration parameters
564#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
565pub enum EcucConfigurationClass {
566    /// Link Time: parts of configuration are delivered from another object code file
567    Link,
568    /// `PostBuild`: a configuration parameter can be changed after compilation
569    PostBuild,
570    /// `PreCompile`: a configuration parameter can not be changed after compilation
571    PreCompile,
572    /// `PublishedInformation` is used to specify the fact that certain information is fixed even before the pre-compile stage.
573    PublishedInformation,
574}
575
576impl From<EcucConfigurationClass> for EnumItem {
577    fn from(value: EcucConfigurationClass) -> Self {
578        match value {
579            EcucConfigurationClass::Link => Self::Link,
580            EcucConfigurationClass::PostBuild => Self::PostBuild,
581            EcucConfigurationClass::PreCompile => Self::PreCompile,
582            EcucConfigurationClass::PublishedInformation => Self::PublishedInformation,
583        }
584    }
585}
586
587impl TryFrom<EnumItem> for EcucConfigurationClass {
588    type Error = AutosarAbstractionError;
589
590    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
591        match value {
592            EnumItem::Link => Ok(Self::Link),
593            EnumItem::PostBuild => Ok(Self::PostBuild),
594            EnumItem::PreCompile => Ok(Self::PreCompile),
595            EnumItem::PublishedInformation => Ok(Self::PublishedInformation),
596            _ => Err(AutosarAbstractionError::ValueConversionError {
597                value: value.to_string(),
598                dest: "EcucConfigurationClass".to_string(),
599            }),
600        }
601    }
602}
603
604//#########################################################
605
606/// The `EcucModuleDefCategory` represents the possible category values for a module definition
607#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
608pub enum EcucModuleDefCategory {
609    /// The module definition is a standardized module (StMD)
610    StandardizedModuleDefinition,
611    /// The module definition is a vendor specific module (VSMD)
612    VendorSpecificModuleDefinition,
613}
614
615impl std::fmt::Display for EcucModuleDefCategory {
616    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
617        match self {
618            EcucModuleDefCategory::StandardizedModuleDefinition => write!(f, "STANDARDIZED_MODULE_DEFINITION"),
619            EcucModuleDefCategory::VendorSpecificModuleDefinition => write!(f, "VENDOR_SPECIFIC_MODULE_DEFINITION"),
620        }
621    }
622}
623
624impl TryFrom<&str> for EcucModuleDefCategory {
625    type Error = AutosarAbstractionError;
626
627    fn try_from(value: &str) -> Result<Self, Self::Error> {
628        match value {
629            "STANDARDIZED_MODULE_DEFINITION" => Ok(Self::StandardizedModuleDefinition),
630            "VENDOR_SPECIFIC_MODULE_DEFINITION" => Ok(Self::VendorSpecificModuleDefinition),
631            _ => Err(AutosarAbstractionError::ValueConversionError {
632                value: value.to_string(),
633                dest: "EcucModuleDefCategory".to_string(),
634            }),
635        }
636    }
637}
638
639//#########################################################
640
641// helper for setting the multiplicity config classes and the value config classes
642fn set_config_classes(
643    base: &Element,
644    element_name_l1: ElementName,
645    element_name_l2: ElementName,
646    config: &[(EcucConfigurationClass, EcucConfigurationVariant)],
647) -> Result<(), AutosarAbstractionError> {
648    // remove the existing multiplicity config classes, since we configure
649    // the entire list instead of updating the existing one
650    let _ = base.remove_sub_element_kind(element_name_l1);
651
652    if !config.is_empty() {
653        // create the new multiplicity config classes
654        let config_classes = base.create_sub_element(element_name_l1)?;
655        for (config_class, variant) in config {
656            let ecuc_config_class_elem = config_classes.create_sub_element(element_name_l2)?;
657            ecuc_config_class_elem
658                .create_sub_element(ElementName::ConfigClass)?
659                .set_character_data::<EnumItem>((*config_class).into())?;
660            ecuc_config_class_elem
661                .create_sub_element(ElementName::ConfigVariant)?
662                .set_character_data::<EnumItem>((*variant).into())?;
663        }
664    }
665
666    Ok(())
667}
668
669// helper for getting the multiplicity config classes and the value config classes
670fn get_config_classes(
671    base: &Element,
672    element_name_l1: ElementName,
673) -> Vec<(EcucConfigurationClass, EcucConfigurationVariant)> {
674    base.get_sub_element(element_name_l1)
675        .into_iter()
676        .flat_map(|config_classes| config_classes.sub_elements())
677        .filter_map(|config_class| {
678            let class = config_class
679                .get_sub_element(ElementName::ConfigClass)?
680                .character_data()?
681                .enum_value()?;
682            let variant = config_class
683                .get_sub_element(ElementName::ConfigVariant)?
684                .character_data()?
685                .enum_value()?;
686            Some((
687                EcucConfigurationClass::try_from(class).ok()?,
688                EcucConfigurationVariant::try_from(variant).ok()?,
689            ))
690        })
691        .collect()
692}
693
694//#########################################################
695
696/// A `EcucDestinationUriDefSet` contains a list of `EcucDestinationUriDef`s
697#[derive(Debug, Clone, PartialEq, Eq, Hash)]
698pub struct EcucDestinationUriDefSet(Element);
699abstraction_element!(EcucDestinationUriDefSet, EcucDestinationUriDefSet);
700impl IdentifiableAbstractionElement for EcucDestinationUriDefSet {}
701
702impl EcucDestinationUriDefSet {
703    pub(crate) fn new(name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
704        let elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
705        let ecuc_destination_uri_def_set_elem =
706            elements.create_named_sub_element(ElementName::EcucDestinationUriDefSet, name)?;
707
708        Ok(Self(ecuc_destination_uri_def_set_elem))
709    }
710
711    /// create a new `EcucDestinationUriDef`
712    pub fn create_destination_uri_def(
713        &self,
714        name: &str,
715        contract: EcucDestinationUriNestingContract,
716    ) -> Result<EcucDestinationUriDef, AutosarAbstractionError> {
717        let defs = self
718            .element()
719            .get_or_create_sub_element(ElementName::DestinationUriDefs)?;
720        EcucDestinationUriDef::new(name, &defs, contract)
721    }
722
723    /// iterate over all destination uri definitions in the set
724    pub fn destination_uri_defs(&self) -> impl Iterator<Item = EcucDestinationUriDef> + Send + use<> {
725        self.element()
726            .get_sub_element(ElementName::DestinationUriDefs)
727            .into_iter()
728            .flat_map(|defs_elem| defs_elem.sub_elements())
729            .filter_map(|def_elem| EcucDestinationUriDef::try_from(def_elem).ok())
730    }
731}
732
733//#########################################################
734
735/// A `EcucDestinationUriDef` defines a target for an `EcucUriReferenceDef`
736#[derive(Debug, Clone, PartialEq, Eq, Hash)]
737pub struct EcucDestinationUriDef(Element);
738abstraction_element!(EcucDestinationUriDef, EcucDestinationUriDef);
739impl IdentifiableAbstractionElement for EcucDestinationUriDef {}
740
741impl EcucDestinationUriDef {
742    /// create a new `EcucDestinationUriDef`
743    pub(crate) fn new(
744        name: &str,
745        parent: &Element,
746        contract: EcucDestinationUriNestingContract,
747    ) -> Result<Self, AutosarAbstractionError> {
748        let ecuc_destination_uri_def_elem =
749            parent.create_named_sub_element(ElementName::EcucDestinationUriDef, name)?;
750
751        let ecuc_destination_uri_def = Self(ecuc_destination_uri_def_elem);
752        ecuc_destination_uri_def.set_nesting_contract(contract)?;
753
754        Ok(ecuc_destination_uri_def)
755    }
756
757    /// set the nesting contract for the destination uri
758    pub fn set_nesting_contract(
759        &self,
760        contract: EcucDestinationUriNestingContract,
761    ) -> Result<(), AutosarAbstractionError> {
762        self.element()
763            .get_or_create_sub_element(ElementName::DestinationUriPolicy)?
764            .get_or_create_sub_element(ElementName::DestinationUriNestingContract)?
765            .set_character_data::<EnumItem>(contract.into())?;
766        Ok(())
767    }
768
769    /// get the nesting contract for the destination uri
770    #[must_use]
771    pub fn nesting_contract(&self) -> Option<EcucDestinationUriNestingContract> {
772        self.element()
773            .get_sub_element(ElementName::DestinationUriPolicy)?
774            .get_sub_element(ElementName::DestinationUriNestingContract)?
775            .character_data()?
776            .enum_value()
777            .and_then(|enum_item| EcucDestinationUriNestingContract::try_from(enum_item).ok())
778    }
779
780    /// create an `EcucParamConfContainerDef` in the destination uri policy
781    pub fn create_param_conf_container_def(
782        &self,
783        name: &str,
784    ) -> Result<EcucParamConfContainerDef, AutosarAbstractionError> {
785        let containers = self
786            .element()
787            .get_or_create_sub_element(ElementName::DestinationUriPolicy)?
788            .get_or_create_sub_element(ElementName::Containers)?;
789        EcucParamConfContainerDef::new(name, &containers)
790    }
791
792    /// create an `EcucChoiceContainerDef` in the destination uri policy
793    pub fn create_choice_container_def(&self, name: &str) -> Result<EcucChoiceContainerDef, AutosarAbstractionError> {
794        let containers = self
795            .element()
796            .get_or_create_sub_element(ElementName::DestinationUriPolicy)?
797            .get_or_create_sub_element(ElementName::Containers)?;
798        EcucChoiceContainerDef::new(name, &containers)
799    }
800
801    /// iterate over all containers in the destination uri policy
802    pub fn containers(&self) -> impl Iterator<Item = EcucContainerDef> + Send + use<> {
803        self.element()
804            .get_sub_element(ElementName::DestinationUriPolicy)
805            .and_then(|dup_elem| dup_elem.get_sub_element(ElementName::Containers))
806            .into_iter()
807            .flat_map(|policy_elem| policy_elem.sub_elements())
808            .filter_map(|container_elem| EcucContainerDef::try_from(container_elem).ok())
809    }
810
811    // theoretically, the destination uri def could also contain parameters or references
812    // it looks like nobody uses these, so we don't implement them here
813}
814
815//#########################################################
816
817/// `EcucDestinationUriNestingContract` provides the different nesting contracts for destination URIs
818#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
819pub enum EcucDestinationUriNestingContract {
820    /// `EcucDestinationUriPolicy` describes elements (subContainers, Parameters, References) that are directly owned by the target container.
821    LeafOfTargetContainer,
822    /// `EcucDestinationUriPolicy` describes the target container of `EcucUriReferenceDef`.
823    TargetContainer,
824    /// `EcucDestinationUriPolicy` describes elements (subContainers, Parameters, References) that are owned by the target container or its subContainers.
825    VertexOfTargetContainer,
826}
827
828impl From<EcucDestinationUriNestingContract> for EnumItem {
829    fn from(value: EcucDestinationUriNestingContract) -> Self {
830        match value {
831            EcucDestinationUriNestingContract::LeafOfTargetContainer => Self::LeafOfTargetContainer,
832            EcucDestinationUriNestingContract::TargetContainer => Self::TargetContainer,
833            EcucDestinationUriNestingContract::VertexOfTargetContainer => Self::VertexOfTargetContainer,
834        }
835    }
836}
837
838impl TryFrom<EnumItem> for EcucDestinationUriNestingContract {
839    type Error = AutosarAbstractionError;
840
841    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
842        match value {
843            EnumItem::LeafOfTargetContainer => Ok(Self::LeafOfTargetContainer),
844            EnumItem::TargetContainer => Ok(Self::TargetContainer),
845            EnumItem::VertexOfTargetContainer => Ok(Self::VertexOfTargetContainer),
846            _ => Err(AutosarAbstractionError::ValueConversionError {
847                value: value.to_string(),
848                dest: "EcucDestinationUriNestingContract".to_string(),
849            }),
850        }
851    }
852}
853
854//#########################################################
855
856#[cfg(test)]
857mod test {
858    use super::*;
859    use crate::AutosarModelAbstraction;
860    use autosar_data::AutosarVersion;
861
862    #[test]
863    fn ecuc_module_def() {
864        let model = AutosarModelAbstraction::create("file.arxml", AutosarVersion::LATEST);
865        let package = model.get_or_create_package("/package").unwrap();
866
867        let ecuc_definition_collection = package.create_ecuc_definition_collection("collection").unwrap();
868        assert_eq!(ecuc_definition_collection.module_defs().count(), 0);
869
870        let ecuc_module_def = package.create_ecuc_module_def("module_def").unwrap();
871        ecuc_definition_collection.add_module_def(&ecuc_module_def).unwrap();
872        assert_eq!(ecuc_definition_collection.module_defs().count(), 1);
873
874        assert_eq!(ecuc_module_def.api_service_prefix(), None);
875        assert_eq!(
876            ecuc_module_def.supported_config_variants(),
877            Vec::<EcucConfigurationVariant>::new()
878        );
879        assert_eq!(ecuc_module_def.post_build_variant_support(), None);
880        assert_eq!(ecuc_module_def.category(), None);
881        assert_eq!(ecuc_module_def.refined_module_def(), None);
882        assert_eq!(ecuc_module_def.lower_multiplicity(), None);
883        assert_eq!(ecuc_module_def.upper_multiplicity(), None);
884        assert_eq!(ecuc_module_def.upper_multiplicity_infinite(), None);
885
886        let base_ecuc_module_def = package.create_ecuc_module_def("base_module_def").unwrap();
887        ecuc_module_def
888            .set_refined_module_def(Some(&base_ecuc_module_def))
889            .unwrap();
890        assert_eq!(ecuc_module_def.refined_module_def(), Some(base_ecuc_module_def.clone()));
891        ecuc_module_def.set_api_service_prefix(Some("prefix")).unwrap();
892        assert_eq!(ecuc_module_def.api_service_prefix(), Some("prefix".to_string()));
893        ecuc_module_def
894            .set_supported_config_variants(&[
895                EcucConfigurationVariant::PreconfiguredConfiguration,
896                EcucConfigurationVariant::VariantLinkTime,
897            ])
898            .unwrap();
899        assert_eq!(
900            ecuc_module_def.supported_config_variants(),
901            vec![
902                EcucConfigurationVariant::PreconfiguredConfiguration,
903                EcucConfigurationVariant::VariantLinkTime
904            ]
905        );
906        ecuc_module_def.set_post_build_variant_support(Some(true)).unwrap();
907        assert_eq!(ecuc_module_def.post_build_variant_support(), Some(true));
908        ecuc_module_def
909            .set_category(Some(EcucModuleDefCategory::VendorSpecificModuleDefinition))
910            .unwrap();
911        assert_eq!(
912            ecuc_module_def.category(),
913            Some(EcucModuleDefCategory::VendorSpecificModuleDefinition)
914        );
915        ecuc_module_def.set_lower_multiplicity(Some(1)).unwrap();
916        assert_eq!(ecuc_module_def.lower_multiplicity(), Some(1));
917        ecuc_module_def.set_upper_multiplicity(Some(2)).unwrap();
918        assert_eq!(ecuc_module_def.upper_multiplicity(), Some(2));
919        ecuc_module_def.set_upper_multiplicity_infinite(Some(true)).unwrap();
920        assert_eq!(ecuc_module_def.upper_multiplicity_infinite(), Some(true));
921    }
922
923    #[test]
924    fn ecuc_configuration_variant_enum_conversion() {
925        let variants = [
926            EcucConfigurationVariant::PreconfiguredConfiguration,
927            EcucConfigurationVariant::RecommendedConfiguration,
928            EcucConfigurationVariant::VariantLinkTime,
929            EcucConfigurationVariant::VariantPostBuild,
930            EcucConfigurationVariant::VariantPreCompile,
931            EcucConfigurationVariant::VariantPostBuildLoadable,
932            EcucConfigurationVariant::VariantPostBuildSelectable,
933        ];
934
935        for variant in &variants {
936            let enum_item: EnumItem = (*variant).into();
937            let converted_variant = EcucConfigurationVariant::try_from(enum_item).unwrap();
938            assert_eq!(*variant, converted_variant);
939        }
940    }
941
942    #[test]
943    fn destionation_uri_defs() {
944        let model = AutosarModelAbstraction::create("file.arxml", AutosarVersion::LATEST);
945        let package = model.get_or_create_package("/package").unwrap();
946
947        let ecuc_destination_uri_def_set = package
948            .create_ecuc_destination_uri_def_set("destination_uri_def_set")
949            .unwrap();
950        assert_eq!(ecuc_destination_uri_def_set.destination_uri_defs().count(), 0);
951
952        let ecuc_destination_uri_def = ecuc_destination_uri_def_set
953            .create_destination_uri_def(
954                "destination_uri_def",
955                EcucDestinationUriNestingContract::LeafOfTargetContainer,
956            )
957            .unwrap();
958        assert_eq!(ecuc_destination_uri_def_set.destination_uri_defs().count(), 1);
959
960        assert_eq!(
961            ecuc_destination_uri_def.nesting_contract(),
962            Some(EcucDestinationUriNestingContract::LeafOfTargetContainer)
963        );
964        assert_eq!(ecuc_destination_uri_def.containers().count(), 0);
965
966        let _param_conf_container_def = ecuc_destination_uri_def
967            .create_param_conf_container_def("param_conf_container")
968            .unwrap();
969        assert_eq!(ecuc_destination_uri_def.containers().count(), 1);
970
971        let _choice_container_def = ecuc_destination_uri_def
972            .create_choice_container_def("choice_container")
973            .unwrap();
974        assert_eq!(ecuc_destination_uri_def.containers().count(), 2);
975    }
976}