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