autosar_data_abstraction/software_component/interface/
clientserver.rs

1use crate::{
2    AbstractionElement, ArPackage, AutosarAbstractionError, Element, EnumItem, IdentifiableAbstractionElement,
3    abstraction_element,
4    datatype::{self, AbstractAutosarDataType},
5    get_reference_parents,
6    software_component::{AbstractPortInterface, OperationInvokedEvent, PortPrototype},
7};
8use autosar_data::ElementName;
9use datatype::AutosarDataType;
10
11//##################################################################
12
13/// A `ClientServerInterface` defines a set of operations that can be implemented by a server and called by a client
14///
15/// Use [`ArPackage::create_client_server_interface`] to create a new client server interface
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct ClientServerInterface(pub(crate) Element);
18abstraction_element!(ClientServerInterface, ClientServerInterface);
19impl IdentifiableAbstractionElement for ClientServerInterface {}
20impl AbstractPortInterface for ClientServerInterface {}
21
22impl ClientServerInterface {
23    /// Create a new `ClientServerInterface`
24    pub(crate) fn new(name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
25        let elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
26        let client_server_interface = elements.create_named_sub_element(ElementName::ClientServerInterface, name)?;
27
28        Ok(Self(client_server_interface))
29    }
30
31    /// remove this `ClientServerInterface` from the model
32    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
33        for operation in self.operations() {
34            operation.remove(true)?;
35        }
36
37        let ref_parents = get_reference_parents(self.element())?;
38
39        AbstractionElement::remove(self, deep)?;
40
41        for (named_parent, _parent) in ref_parents {
42            match named_parent.element_name() {
43                ElementName::PPortPrototype | ElementName::RPortPrototype | ElementName::PrPortPrototype => {
44                    if let Ok(port) = PortPrototype::try_from(named_parent) {
45                        port.remove(deep)?;
46                    }
47                }
48                _ => {}
49            }
50        }
51
52        Ok(())
53    }
54
55    /// Add a possible error to the client server interface
56    pub fn create_possible_error(
57        &self,
58        name: &str,
59        error_code: u64,
60    ) -> Result<ApplicationError, AutosarAbstractionError> {
61        let possible_errors = self.element().get_or_create_sub_element(ElementName::PossibleErrors)?;
62        ApplicationError::new(name, error_code, &possible_errors)
63    }
64
65    /// add an operation to the client server interface
66    pub fn create_operation(&self, name: &str) -> Result<ClientServerOperation, AutosarAbstractionError> {
67        let operations = self.element().get_or_create_sub_element(ElementName::Operations)?;
68        ClientServerOperation::new(name, &operations)
69    }
70
71    /// iterate over all operations
72    pub fn operations(&self) -> impl Iterator<Item = ClientServerOperation> + Send + use<> {
73        self.element()
74            .get_sub_element(ElementName::Operations)
75            .into_iter()
76            .flat_map(|operations| operations.sub_elements())
77            .filter_map(|elem| ClientServerOperation::try_from(elem).ok())
78    }
79
80    /// iterate over all application errors
81    pub fn possible_errors(&self) -> impl Iterator<Item = ApplicationError> + Send + use<> {
82        self.element()
83            .get_sub_element(ElementName::PossibleErrors)
84            .into_iter()
85            .flat_map(|errors| errors.sub_elements())
86            .filter_map(|elem| ApplicationError::try_from(elem).ok())
87    }
88}
89
90//##################################################################
91
92/// An `ApplicationError` represents an error that can be returned by a client server operation
93#[derive(Debug, Clone, PartialEq, Eq, Hash)]
94pub struct ApplicationError(Element);
95abstraction_element!(ApplicationError, ApplicationError);
96impl IdentifiableAbstractionElement for ApplicationError {}
97
98impl ApplicationError {
99    /// Create a new `ApplicationError`
100    fn new(name: &str, error_code: u64, parent_element: &Element) -> Result<Self, AutosarAbstractionError> {
101        let application_error = parent_element.create_named_sub_element(ElementName::ApplicationError, name)?;
102        let application_error = Self(application_error);
103        application_error.set_error_code(error_code)?;
104
105        Ok(application_error)
106    }
107
108    /// Set the error code of the application error
109    pub fn set_error_code(&self, error_code: u64) -> Result<(), AutosarAbstractionError> {
110        self.element()
111            .get_or_create_sub_element(ElementName::ErrorCode)?
112            .set_character_data(error_code)?;
113        Ok(())
114    }
115
116    /// Get the error code of the application error
117    #[must_use]
118    pub fn error_code(&self) -> Option<u64> {
119        self.element()
120            .get_sub_element(ElementName::ErrorCode)?
121            .character_data()?
122            .parse_integer()
123    }
124}
125
126//##################################################################
127
128/// A `ClientServerOperation` defines an operation in a `ClientServerInterface`
129#[derive(Debug, Clone, PartialEq, Eq, Hash)]
130pub struct ClientServerOperation(Element);
131abstraction_element!(ClientServerOperation, ClientServerOperation);
132impl IdentifiableAbstractionElement for ClientServerOperation {}
133
134impl ClientServerOperation {
135    /// Create a new `ClientServerOperation`
136    fn new(name: &str, parent_element: &Element) -> Result<Self, AutosarAbstractionError> {
137        let operation = parent_element.create_named_sub_element(ElementName::ClientServerOperation, name)?;
138        Ok(Self(operation))
139    }
140
141    /// Remove this `ClientServerOperation` from the model
142    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
143        let ref_parents = get_reference_parents(self.element())?;
144        AbstractionElement::remove(self, deep)?;
145
146        for (named_parent, _parent) in ref_parents {
147            if named_parent.element_name() == ElementName::OperationInvokedEvent
148                && let Ok(event) = OperationInvokedEvent::try_from(named_parent)
149            {
150                event.remove(deep)?;
151            }
152        }
153
154        Ok(())
155    }
156
157    /// Add an argument to the operation
158    pub fn create_argument<T: AbstractAutosarDataType>(
159        &self,
160        name: &str,
161        data_type: &T,
162        direction: ArgumentDirection,
163    ) -> Result<ArgumentDataPrototype, AutosarAbstractionError> {
164        let arguments = self.element().get_or_create_sub_element(ElementName::Arguments)?;
165        ArgumentDataPrototype::new(name, &arguments, data_type, direction)
166    }
167
168    /// iterate over all arguments
169    pub fn arguments(&self) -> impl Iterator<Item = ArgumentDataPrototype> + Send + use<> {
170        self.element()
171            .get_sub_element(ElementName::Arguments)
172            .into_iter()
173            .flat_map(|arguments| arguments.sub_elements())
174            .filter_map(|elem| ArgumentDataPrototype::try_from(elem).ok())
175    }
176
177    /// add a reference to possible error to the operation
178    pub fn add_possible_error(&self, error: &ApplicationError) -> Result<(), AutosarAbstractionError> {
179        if self.element().named_parent()? != error.element().named_parent()? {
180            return Err(AutosarAbstractionError::InvalidParameter(
181                "Error and operation must be in the same ClientServerInterface".to_string(),
182            ));
183        }
184
185        let possible_errors = self
186            .element()
187            .get_or_create_sub_element(ElementName::PossibleErrorRefs)?;
188        possible_errors
189            .create_sub_element(ElementName::PossibleErrorRef)?
190            .set_reference_target(error.element())?;
191        Ok(())
192    }
193
194    /// Get the possible errors of the operation
195    pub fn possible_errors(&self) -> impl Iterator<Item = ApplicationError> + Send + use<> {
196        self.element()
197            .get_sub_element(ElementName::PossibleErrorRefs)
198            .into_iter()
199            .flat_map(|errors| errors.sub_elements())
200            .filter_map(|refelem| {
201                refelem
202                    .get_reference_target()
203                    .ok()
204                    .and_then(|elem| ApplicationError::try_from(elem).ok())
205            })
206    }
207}
208
209//##################################################################
210
211/// The `ArgumentDirection` defines the direction of an argument in a `ClientServerOperation`
212///
213/// Input arguments are used to pass data from the client to the server and are usualy passed by value.
214/// Output arguments are used to pass data from the server to the client and are usually passed by reference.
215/// In/Out arguments are used to pass data in both directions and are usually passed by reference.
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
217pub enum ArgumentDirection {
218    /// The argument is an input argument
219    In,
220    /// The argument is an output argument
221    Out,
222    /// The argument is an in/out argument
223    InOut,
224}
225
226impl TryFrom<EnumItem> for ArgumentDirection {
227    type Error = AutosarAbstractionError;
228
229    fn try_from(item: EnumItem) -> Result<Self, Self::Error> {
230        match item {
231            EnumItem::In => Ok(ArgumentDirection::In),
232            EnumItem::Out => Ok(ArgumentDirection::Out),
233            EnumItem::Inout => Ok(ArgumentDirection::InOut),
234            _ => Err(AutosarAbstractionError::ValueConversionError {
235                value: item.to_string(),
236                dest: "ArgumentDirection".to_string(),
237            }),
238        }
239    }
240}
241
242impl From<ArgumentDirection> for EnumItem {
243    fn from(direction: ArgumentDirection) -> Self {
244        match direction {
245            ArgumentDirection::In => EnumItem::In,
246            ArgumentDirection::Out => EnumItem::Out,
247            ArgumentDirection::InOut => EnumItem::Inout,
248        }
249    }
250}
251
252//##################################################################
253
254/// An `ArgumentDataPrototype` represents an argument in a `ClientServerOperation`
255#[derive(Debug, Clone, PartialEq, Eq, Hash)]
256pub struct ArgumentDataPrototype(Element);
257abstraction_element!(ArgumentDataPrototype, ArgumentDataPrototype);
258impl IdentifiableAbstractionElement for ArgumentDataPrototype {}
259
260impl ArgumentDataPrototype {
261    /// Create a new `ArgumentDataPrototype`
262    fn new<T: AbstractAutosarDataType>(
263        name: &str,
264        parent_element: &Element,
265        data_type: &T,
266        direction: ArgumentDirection,
267    ) -> Result<Self, AutosarAbstractionError> {
268        let argument = parent_element.create_named_sub_element(ElementName::ArgumentDataPrototype, name)?;
269        let argument = Self(argument);
270        argument.set_data_type(data_type)?;
271        argument.set_direction(direction)?;
272
273        Ok(argument)
274    }
275
276    /// Set the data type of the argument
277    pub fn set_data_type<T: AbstractAutosarDataType>(&self, data_type: &T) -> Result<(), AutosarAbstractionError> {
278        self.element()
279            .get_or_create_sub_element(ElementName::TypeTref)?
280            .set_reference_target(data_type.element())?;
281        Ok(())
282    }
283
284    /// Get the data type of the argument
285    #[must_use]
286    pub fn data_type(&self) -> Option<AutosarDataType> {
287        let data_type_elem = self
288            .element()
289            .get_sub_element(ElementName::TypeTref)?
290            .get_reference_target()
291            .ok()?;
292        AutosarDataType::try_from(data_type_elem).ok()
293    }
294
295    /// Set the direction of the argument
296    pub fn set_direction(&self, direction: ArgumentDirection) -> Result<(), AutosarAbstractionError> {
297        self.element()
298            .get_or_create_sub_element(ElementName::Direction)?
299            .set_character_data::<EnumItem>(direction.into())?;
300        Ok(())
301    }
302
303    /// Get the direction of the argument
304    #[must_use]
305    pub fn direction(&self) -> Option<ArgumentDirection> {
306        let value = self
307            .element()
308            .get_sub_element(ElementName::Direction)?
309            .character_data()?
310            .enum_value()?;
311
312        ArgumentDirection::try_from(value).ok()
313    }
314}
315
316//##################################################################
317
318#[cfg(test)]
319mod test {
320    use super::*;
321    use crate::{AutosarModelAbstraction, software_component::AbstractSwComponentType};
322    use autosar_data::AutosarVersion;
323    use datatype::{BaseTypeEncoding, ImplementationDataTypeSettings};
324
325    #[test]
326    fn test_client_server_interface() {
327        let model = AutosarModelAbstraction::create("filename", AutosarVersion::LATEST);
328        let package = model.get_or_create_package("/package").unwrap();
329        let client_server_interface = ClientServerInterface::new("TestInterface", &package).unwrap();
330
331        assert_eq!(client_server_interface.name().unwrap(), "TestInterface");
332        assert_eq!(client_server_interface.operations().count(), 0);
333        assert_eq!(client_server_interface.possible_errors().count(), 0);
334
335        let error = client_server_interface.create_possible_error("TestError", 42).unwrap();
336        assert_eq!(client_server_interface.possible_errors().count(), 1);
337        assert_eq!(error.name().unwrap(), "TestError");
338        assert_eq!(error.error_code().unwrap(), 42);
339
340        let operation = client_server_interface.create_operation("TestOperation").unwrap();
341        assert_eq!(client_server_interface.operations().count(), 1);
342        assert_eq!(operation.name().unwrap(), "TestOperation");
343        assert_eq!(operation.arguments().count(), 0);
344
345        operation.add_possible_error(&error).unwrap();
346        assert_eq!(operation.possible_errors().count(), 1);
347
348        let base_type = package
349            .create_sw_base_type("base", 32, BaseTypeEncoding::None, None, None, None)
350            .unwrap();
351        let impl_settings = ImplementationDataTypeSettings::Value {
352            name: "ImplementationValue".to_string(),
353            base_type,
354            compu_method: None,
355            data_constraint: None,
356        };
357        let datatype = package.create_implementation_data_type(&impl_settings).unwrap();
358        let argument = operation
359            .create_argument("TestArgument", &datatype, ArgumentDirection::In)
360            .unwrap();
361        assert_eq!(argument.name().unwrap(), "TestArgument");
362        assert_eq!(argument.data_type().unwrap().name().unwrap(), "ImplementationValue");
363        assert_eq!(argument.direction().unwrap(), ArgumentDirection::In);
364        assert_eq!(operation.arguments().count(), 1);
365
366        client_server_interface.set_is_service(Some(true)).unwrap();
367        assert!(client_server_interface.is_service().unwrap());
368        client_server_interface.set_is_service(Some(false)).unwrap();
369        assert!(!client_server_interface.is_service().unwrap());
370        client_server_interface.set_is_service(None).unwrap();
371        assert_eq!(client_server_interface.is_service(), None);
372    }
373
374    #[test]
375    fn remove() {
376        let model = AutosarModelAbstraction::create("filename", AutosarVersion::LATEST);
377        let package = model.get_or_create_package("/package").unwrap();
378        let client_server_interface = ClientServerInterface::new("TestInterface", &package).unwrap();
379
380        let composition_type = package.create_composition_sw_component_type("comp_parent").unwrap();
381        let _composition_r_port = composition_type
382            .create_r_port("port_r", &client_server_interface)
383            .unwrap();
384
385        assert_eq!(composition_type.ports().count(), 1);
386        client_server_interface.remove(true).unwrap();
387        assert_eq!(composition_type.ports().count(), 0);
388    }
389}