autosar_data_abstraction/ecu_configuration/definition/
reference.rs

1use crate::{
2    AbstractionElement, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element,
3    ecu_configuration::{EcucCommonAttributes, EcucDefinitionElement},
4};
5use autosar_data::{Element, ElementName};
6
7use super::{AbstractEcucContainerDef, EcucContainerDef, EcucDestinationUriDef};
8
9//#########################################################
10
11/// marker trait for all reference definitions
12pub trait AbstractEcucReferenceDef: AbstractionElement + EcucCommonAttributes + EcucDefinitionElement {}
13
14//#########################################################
15
16/// The `EcucForeignReferenceDef` specifies a reference to an XML description of an entity
17/// described in another AUTOSAR template.
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19pub struct EcucForeignReferenceDef(Element);
20abstraction_element!(EcucForeignReferenceDef, EcucForeignReferenceDef);
21impl IdentifiableAbstractionElement for EcucForeignReferenceDef {}
22impl EcucCommonAttributes for EcucForeignReferenceDef {}
23impl EcucDefinitionElement for EcucForeignReferenceDef {}
24impl AbstractEcucReferenceDef for EcucForeignReferenceDef {}
25
26impl EcucForeignReferenceDef {
27    pub(crate) fn new(name: &str, references_elem: &Element, origin: &str) -> Result<Self, AutosarAbstractionError> {
28        let ecuc_foreign_reference_def_elem =
29            references_elem.create_named_sub_element(ElementName::EcucForeignReferenceDef, name)?;
30        let ecuc_foreign_reference_def = Self(ecuc_foreign_reference_def_elem);
31        ecuc_foreign_reference_def.set_origin(origin)?;
32
33        Ok(ecuc_foreign_reference_def)
34    }
35
36    /// set the destination type of the reference definition
37    pub fn set_destination_type(&self, destination_type: Option<&str>) -> Result<(), AutosarAbstractionError> {
38        if let Some(destination_type) = destination_type {
39            self.element()
40                .get_or_create_sub_element(ElementName::DestinationType)?
41                .set_character_data(destination_type)?;
42        } else {
43            let _ = self.element().remove_sub_element_kind(ElementName::DestinationType);
44        }
45
46        Ok(())
47    }
48
49    /// get the destination type of the reference definition
50    #[must_use]
51    pub fn destination_type(&self) -> Option<String> {
52        self.element()
53            .get_sub_element(ElementName::DestinationType)?
54            .character_data()?
55            .string_value()
56    }
57}
58
59//#########################################################
60
61/// The `EcucInstanceReferenceDef` specifies a reference to an XML description of an entity
62/// described in another AUTOSAR template using INSTANCE REFERENCE semantics.
63#[derive(Debug, Clone, PartialEq, Eq, Hash)]
64pub struct EcucInstanceReferenceDef(Element);
65abstraction_element!(EcucInstanceReferenceDef, EcucInstanceReferenceDef);
66impl IdentifiableAbstractionElement for EcucInstanceReferenceDef {}
67impl EcucCommonAttributes for EcucInstanceReferenceDef {}
68impl EcucDefinitionElement for EcucInstanceReferenceDef {}
69impl AbstractEcucReferenceDef for EcucInstanceReferenceDef {}
70
71impl EcucInstanceReferenceDef {
72    pub(crate) fn new(name: &str, references_elem: &Element, origin: &str) -> Result<Self, AutosarAbstractionError> {
73        let ecuc_instance_reference_def_elem =
74            references_elem.create_named_sub_element(ElementName::EcucInstanceReferenceDef, name)?;
75        let ecuc_instance_reference_def = Self(ecuc_instance_reference_def_elem);
76        ecuc_instance_reference_def.set_origin(origin)?;
77
78        Ok(ecuc_instance_reference_def)
79    }
80
81    /// set the destination type of the reference definition
82    pub fn set_destination_type(&self, destination_type: Option<&str>) -> Result<(), AutosarAbstractionError> {
83        if let Some(destination_type) = destination_type {
84            self.element()
85                .get_or_create_sub_element(ElementName::DestinationType)?
86                .set_character_data(destination_type)?;
87        } else {
88            let _ = self.element().remove_sub_element_kind(ElementName::DestinationType);
89        }
90
91        Ok(())
92    }
93
94    /// get the destination type of the reference definition
95    #[must_use]
96    pub fn destination_type(&self) -> Option<String> {
97        self.element()
98            .get_sub_element(ElementName::DestinationType)?
99            .character_data()?
100            .string_value()
101    }
102
103    /// set the destination context of the reference definition
104    ///
105    /// The destination context is a string of autosar element names separated by spaces.
106    /// Additionally, the '*' character can be used to indicate multiple occurrences of the previous element.
107    /// E.g. "SW-COMPONENT-PROTOTYPE* R-PORT-PROTOTYPE"
108    pub fn set_destination_context(&self, destination_context: Option<&str>) -> Result<(), AutosarAbstractionError> {
109        if let Some(destination_context) = destination_context {
110            self.element()
111                .get_or_create_sub_element(ElementName::DestinationContext)?
112                .set_character_data(destination_context)?;
113        } else {
114            let _ = self.element().remove_sub_element_kind(ElementName::DestinationContext);
115        }
116
117        Ok(())
118    }
119
120    /// get the destination context of the reference definition
121    ///
122    /// The destination context is a string of autosar element names separated by spaces.
123    #[must_use]
124    pub fn destination_context(&self) -> Option<String> {
125        self.element()
126            .get_sub_element(ElementName::DestinationContext)?
127            .character_data()?
128            .string_value()
129    }
130}
131
132//#########################################################
133
134/// The `EcucChoiceReferenceDef` specifies alternative references where only one of the specified
135/// references will be used in the ECU configuration.
136#[derive(Debug, Clone, PartialEq, Eq, Hash)]
137pub struct EcucChoiceReferenceDef(Element);
138abstraction_element!(EcucChoiceReferenceDef, EcucChoiceReferenceDef);
139impl IdentifiableAbstractionElement for EcucChoiceReferenceDef {}
140impl EcucCommonAttributes for EcucChoiceReferenceDef {}
141impl EcucDefinitionElement for EcucChoiceReferenceDef {}
142impl AbstractEcucReferenceDef for EcucChoiceReferenceDef {}
143
144impl EcucChoiceReferenceDef {
145    pub(crate) fn new(name: &str, references_elem: &Element, origin: &str) -> Result<Self, AutosarAbstractionError> {
146        let ecu_choice_reference_def_elem =
147            references_elem.create_named_sub_element(ElementName::EcucChoiceReferenceDef, name)?;
148        let ecu_choice_reference_def = Self(ecu_choice_reference_def_elem);
149        ecu_choice_reference_def.set_origin(origin)?;
150
151        Ok(ecu_choice_reference_def)
152    }
153
154    /// add a reference to a destination container
155    pub fn add_destination<T: AbstractEcucContainerDef>(&self, destination: &T) -> Result<(), AutosarAbstractionError> {
156        let dest_refs = self.element().get_or_create_sub_element(ElementName::DestinationRefs)?;
157        dest_refs
158            .create_sub_element(ElementName::DestinationRef)?
159            .set_reference_target(destination.element())?;
160
161        Ok(())
162    }
163
164    /// get the references to the destination containers
165    pub fn destination_refs(&self) -> impl Iterator<Item = EcucContainerDef> + Send + 'static {
166        self.element()
167            .get_sub_element(ElementName::DestinationRefs)
168            .into_iter()
169            .flat_map(|dest_refs| dest_refs.sub_elements())
170            .filter_map(|dest_ref| {
171                dest_ref
172                    .get_reference_target()
173                    .ok()
174                    .and_then(|elem| elem.try_into().ok())
175            })
176    }
177}
178
179//#########################################################
180
181/// The `EcuReferenceDef` specifies references between parameters in the ECU configuration.
182#[derive(Debug, Clone, PartialEq, Eq, Hash)]
183pub struct EcucReferenceDef(Element);
184abstraction_element!(EcucReferenceDef, EcucReferenceDef);
185impl IdentifiableAbstractionElement for EcucReferenceDef {}
186impl EcucCommonAttributes for EcucReferenceDef {}
187impl EcucDefinitionElement for EcucReferenceDef {}
188impl AbstractEcucReferenceDef for EcucReferenceDef {}
189
190impl EcucReferenceDef {
191    pub(crate) fn new(name: &str, references_elem: &Element, origin: &str) -> Result<Self, AutosarAbstractionError> {
192        let ecu_reference_def_elem = references_elem.create_named_sub_element(ElementName::EcucReferenceDef, name)?;
193        let ecu_reference_def = Self(ecu_reference_def_elem);
194        ecu_reference_def.set_origin(origin)?;
195
196        Ok(ecu_reference_def)
197    }
198
199    /// set the destination container of the reference
200    pub fn set_destination<T: AbstractEcucContainerDef>(
201        &self,
202        destination: Option<&T>,
203    ) -> Result<(), AutosarAbstractionError> {
204        if let Some(destination) = destination {
205            self.element()
206                .get_or_create_sub_element(ElementName::DestinationRef)?
207                .set_reference_target(destination.element())?;
208        } else {
209            let _ = self.element().remove_sub_element_kind(ElementName::DestinationRef);
210        }
211
212        Ok(())
213    }
214
215    /// get the destination container of the reference
216    #[must_use]
217    pub fn destination(&self) -> Option<EcucContainerDef> {
218        self.element()
219            .get_sub_element(ElementName::DestinationRef)
220            .and_then(|dest_ref| dest_ref.get_reference_target().ok())
221            .and_then(|elem| elem.try_into().ok())
222    }
223}
224
225//#########################################################
226
227/// The `EcucUriReferenceDef` defines a reference with a destination that is specified via a destinationUri
228#[derive(Debug, Clone, PartialEq, Eq, Hash)]
229pub struct EcucUriReferenceDef(Element);
230abstraction_element!(EcucUriReferenceDef, EcucUriReferenceDef);
231impl IdentifiableAbstractionElement for EcucUriReferenceDef {}
232impl EcucCommonAttributes for EcucUriReferenceDef {}
233impl EcucDefinitionElement for EcucUriReferenceDef {}
234impl AbstractEcucReferenceDef for EcucUriReferenceDef {}
235
236impl EcucUriReferenceDef {
237    pub(crate) fn new(name: &str, references_elem: &Element, origin: &str) -> Result<Self, AutosarAbstractionError> {
238        let ecu_uri_reference_def_elem =
239            references_elem.create_named_sub_element(ElementName::EcucUriReferenceDef, name)?;
240        let ecu_uri_reference_def = Self(ecu_uri_reference_def_elem);
241        ecu_uri_reference_def.set_origin(origin)?;
242
243        Ok(ecu_uri_reference_def)
244    }
245
246    /// set the destination uri of the reference definition
247    pub fn set_destination_uri(
248        &self,
249        destination_uri: Option<&EcucDestinationUriDef>,
250    ) -> Result<(), AutosarAbstractionError> {
251        if let Some(destination_uri) = destination_uri {
252            self.element()
253                .get_or_create_sub_element(ElementName::DestinationUriRef)?
254                .set_reference_target(destination_uri.element())?;
255        } else {
256            let _ = self.element().remove_sub_element_kind(ElementName::DestinationUriRef);
257        }
258
259        Ok(())
260    }
261
262    /// get the destination uri of the reference definition
263    #[must_use]
264    pub fn destination_uri(&self) -> Option<EcucDestinationUriDef> {
265        self.element()
266            .get_sub_element(ElementName::DestinationUriRef)?
267            .get_reference_target()
268            .ok()?
269            .try_into()
270            .ok()
271    }
272}
273
274//#########################################################
275
276/// `EcucAnyReferenceDef` is an an enum of all possible reference definitions
277/// It is used as a return type by the iterator over all references in a container
278#[derive(Debug, Clone, PartialEq, Eq, Hash)]
279pub enum EcucAnyReferenceDef {
280    /// the reference is a foreign reference (external reference)
281    Foreign(EcucForeignReferenceDef),
282    /// the reference is an instance reference (external reference)
283    Instance(EcucInstanceReferenceDef),
284    /// the reference is a choice reference (internal reference)
285    Choice(EcucChoiceReferenceDef),
286    /// the reference is a normal reference (internal reference)
287    Normal(EcucReferenceDef),
288    /// the reference is a uri reference (internal reference)
289    Uri(EcucUriReferenceDef),
290}
291
292impl AbstractionElement for EcucAnyReferenceDef {
293    fn element(&self) -> &Element {
294        match self {
295            EcucAnyReferenceDef::Foreign(elem) => elem.element(),
296            EcucAnyReferenceDef::Instance(elem) => elem.element(),
297            EcucAnyReferenceDef::Choice(elem) => elem.element(),
298            EcucAnyReferenceDef::Normal(elem) => elem.element(),
299            EcucAnyReferenceDef::Uri(elem) => elem.element(),
300        }
301    }
302}
303
304impl TryFrom<Element> for EcucAnyReferenceDef {
305    type Error = AutosarAbstractionError;
306
307    fn try_from(element: Element) -> Result<Self, Self::Error> {
308        match element.element_name() {
309            ElementName::EcucForeignReferenceDef => Ok(EcucAnyReferenceDef::Foreign(element.try_into()?)),
310            ElementName::EcucInstanceReferenceDef => Ok(EcucAnyReferenceDef::Instance(element.try_into()?)),
311            ElementName::EcucChoiceReferenceDef => Ok(EcucAnyReferenceDef::Choice(element.try_into()?)),
312            ElementName::EcucReferenceDef => Ok(EcucAnyReferenceDef::Normal(element.try_into()?)),
313            ElementName::EcucUriReferenceDef => Ok(EcucAnyReferenceDef::Uri(element.try_into()?)),
314            _ => Err(AutosarAbstractionError::ConversionError {
315                element,
316                dest: "EcucAnyReferenceDef".to_string(),
317            }),
318        }
319    }
320}
321
322impl IdentifiableAbstractionElement for EcucAnyReferenceDef {}
323impl EcucDefinitionElement for EcucAnyReferenceDef {}
324impl EcucCommonAttributes for EcucAnyReferenceDef {}
325impl AbstractEcucReferenceDef for EcucAnyReferenceDef {}
326
327//#########################################################
328
329#[cfg(test)]
330mod test {
331    use crate::{
332        AbstractionElement, AutosarModelAbstraction,
333        ecu_configuration::{EcucContainerDef, EcucDestinationUriNestingContract},
334    };
335    use autosar_data::AutosarVersion;
336
337    #[test]
338    fn test_foreign_reference_def() {
339        let model = AutosarModelAbstraction::create("file.arxml", AutosarVersion::LATEST);
340        let pkg = model.get_or_create_package("/pkg").unwrap();
341
342        let ecuc_module_def = pkg.create_ecuc_module_def("module").unwrap();
343        let container = ecuc_module_def.create_param_conf_container_def("container").unwrap();
344        let foreign_ref = container.create_foreign_reference_def("foreign_ref", "origin").unwrap();
345        assert_eq!(container.references().count(), 1);
346
347        assert_eq!(foreign_ref.destination_type(), None);
348        foreign_ref.set_destination_type(Some("type")).unwrap();
349        assert_eq!(foreign_ref.destination_type(), Some("type".to_string()));
350        assert_eq!(container.references().next().unwrap().element(), foreign_ref.element());
351    }
352
353    #[test]
354    fn test_instance_reference_def() {
355        let model = AutosarModelAbstraction::create("file.arxml", AutosarVersion::LATEST);
356        let pkg = model.get_or_create_package("/pkg").unwrap();
357
358        let ecuc_module_def = pkg.create_ecuc_module_def("module").unwrap();
359        let container = ecuc_module_def.create_param_conf_container_def("container").unwrap();
360        let instance_ref = container
361            .create_instance_reference_def("instance_ref", "origin")
362            .unwrap();
363        assert_eq!(container.references().count(), 1);
364
365        assert_eq!(instance_ref.destination_type(), None);
366        instance_ref.set_destination_type(Some("type")).unwrap();
367        assert_eq!(instance_ref.destination_type(), Some("type".to_string()));
368
369        assert_eq!(instance_ref.destination_context(), None);
370        instance_ref.set_destination_context(Some("context")).unwrap();
371        assert_eq!(instance_ref.destination_context(), Some("context".to_string()));
372        assert_eq!(container.references().next().unwrap().element(), instance_ref.element());
373    }
374
375    #[test]
376    fn test_choice_reference_def() {
377        let model = AutosarModelAbstraction::create("file.arxml", AutosarVersion::LATEST);
378        let pkg = model.get_or_create_package("/pkg").unwrap();
379
380        let ecuc_module_def = pkg.create_ecuc_module_def("module").unwrap();
381        let container = ecuc_module_def.create_param_conf_container_def("container").unwrap();
382        let choice_ref = container.create_choice_reference_def("choice_ref", "origin").unwrap();
383        assert_eq!(container.references().count(), 1);
384
385        assert_eq!(choice_ref.destination_refs().count(), 0);
386        let dest = container.create_param_conf_container_def("dest").unwrap();
387        choice_ref.add_destination(&dest).unwrap();
388        assert_eq!(choice_ref.destination_refs().count(), 1);
389        assert_eq!(container.references().next().unwrap().element(), choice_ref.element());
390    }
391
392    #[test]
393    fn test_reference_def() {
394        let model = AutosarModelAbstraction::create("file.arxml", AutosarVersion::LATEST);
395        let pkg = model.get_or_create_package("/pkg").unwrap();
396
397        let ecuc_module_def = pkg.create_ecuc_module_def("module").unwrap();
398        let container = ecuc_module_def.create_param_conf_container_def("container").unwrap();
399        let reference = container.create_reference_def("reference", "origin").unwrap();
400        assert_eq!(container.references().count(), 1);
401
402        assert_eq!(reference.destination(), None);
403        let dest = container.create_param_conf_container_def("dest").unwrap();
404        reference.set_destination(Some(&dest)).unwrap();
405        assert_eq!(reference.destination().unwrap(), EcucContainerDef::ParamConf(dest));
406        assert_eq!(container.references().next().unwrap().element(), reference.element());
407    }
408
409    #[test]
410    fn test_uri_reference_def() {
411        let model = AutosarModelAbstraction::create("file.arxml", AutosarVersion::LATEST);
412        let pkg = model.get_or_create_package("/pkg").unwrap();
413
414        let ecuc_module_def = pkg.create_ecuc_module_def("module").unwrap();
415        let container = ecuc_module_def.create_param_conf_container_def("container").unwrap();
416        let uri_ref = container.create_uri_reference_def("uri_ref", "origin").unwrap();
417        assert_eq!(container.references().count(), 1);
418
419        let uri_def_set = pkg.create_ecuc_destination_uri_def_set("uri_def").unwrap();
420        let uri_def = uri_def_set
421            .create_destination_uri_def("uri", EcucDestinationUriNestingContract::VertexOfTargetContainer)
422            .unwrap();
423
424        assert_eq!(uri_ref.destination_uri(), None);
425        uri_ref.set_destination_uri(Some(&uri_def)).unwrap();
426        assert_eq!(uri_ref.destination_uri(), Some(uri_def));
427        assert_eq!(container.references().next().unwrap().element(), uri_ref.element());
428    }
429}