autosar_data_abstraction/
lib.rs1#![warn(missing_docs)]
80
81use std::path::Path;
82
83use autosar_data::{
84 ArxmlFile, AutosarDataError, AutosarModel, AutosarVersion, Element, ElementName, EnumItem, WeakElement,
85};
86use thiserror::Error;
87
88pub mod communication;
90pub mod datatype;
91pub mod ecu_configuration;
92pub mod software_component;
93
94mod arpackage;
96mod ecuinstance;
97mod system;
98
99pub use arpackage::{ArPackage, ReferenceBase};
101pub use ecuinstance::*;
102pub use system::*;
103
104#[derive(Error, Debug)]
106#[non_exhaustive]
107pub enum AutosarAbstractionError {
108 #[error("conversion error: could not convert {} to {}", .element.element_name(), dest)]
110 ConversionError {
111 element: Element,
113 dest: String,
115 },
116
117 #[error("value conversion error: could not convert {} to {}", .value, .dest)]
119 ValueConversionError {
120 value: String,
122 dest: String,
124 },
125
126 #[error("model error: {}", .0)]
129 ModelError(AutosarDataError),
130
131 #[error("invalid path: {}", .0)]
133 InvalidPath(String),
134
135 #[error("the item already exists")]
137 ItemAlreadyExists,
138
139 #[error("invalid parameter: {}", .0)]
141 InvalidParameter(String),
142}
143
144impl From<AutosarDataError> for AutosarAbstractionError {
145 fn from(err: AutosarDataError) -> Self {
146 AutosarAbstractionError::ModelError(err)
147 }
148}
149
150pub trait AbstractionElement: Clone + PartialEq + TryFrom<autosar_data::Element> {
154 #[must_use]
156 fn element(&self) -> ∈
157
158 fn remove(self, _deep: bool) -> Result<(), AutosarAbstractionError> {
162 let element = self.element();
163 let Some(parent) = element.parent()? else {
164 return Err(AutosarAbstractionError::InvalidParameter(
166 "cannot remove root element".to_string(),
167 ));
168 };
169
170 if element.is_identifiable() {
171 let model = element.model()?;
172 let path = element.path()?;
173 let inbound_refs = model.get_references_to(&path);
174 for ref_elem in inbound_refs.iter().filter_map(WeakElement::upgrade) {
175 let Ok(Some(parent)) = ref_elem.parent() else {
176 continue;
177 };
178 match ref_elem.element_name() {
179 ElementName::FibexElementRef => {
180 if let Ok(Some(grandparent)) = parent.parent() {
182 grandparent.remove_sub_element(parent)?;
183 }
184 }
185 _ => {
186 let _ = parent.remove_sub_element(ref_elem);
190 }
191 }
192 }
193 }
194
195 parent.remove_sub_element(element.clone())?;
196 Ok(())
197 }
198}
199
200pub trait IdentifiableAbstractionElement: AbstractionElement {
202 #[must_use]
204 fn name(&self) -> Option<String> {
205 self.element().item_name()
206 }
207
208 fn set_name(&self, name: &str) -> Result<(), AutosarAbstractionError> {
210 self.element().set_item_name(name)?;
211 Ok(())
212 }
213}
214
215macro_rules! abstraction_element {
216 ($name: ident, $base_elem: ident) => {
217 impl TryFrom<autosar_data::Element> for $name {
218 type Error = AutosarAbstractionError;
219
220 fn try_from(element: autosar_data::Element) -> Result<Self, Self::Error> {
221 if element.element_name() == autosar_data::ElementName::$base_elem {
222 Ok($name(element))
223 } else {
224 Err(AutosarAbstractionError::ConversionError {
225 element,
226 dest: stringify!($name).to_string(),
227 })
228 }
229 }
230 }
231
232 impl AbstractionElement for $name {
233 fn element(&self) -> &autosar_data::Element {
234 &self.0
235 }
236 }
237
238 impl From<$name> for autosar_data::Element {
239 fn from(val: $name) -> Self {
240 val.0
241 }
242 }
243 };
244}
245
246pub(crate) use abstraction_element;
247
248#[derive(Debug, Clone, PartialEq, Eq)]
252pub struct AutosarModelAbstraction(AutosarModel);
253
254impl AutosarModelAbstraction {
255 #[must_use]
257 pub fn new(model: AutosarModel) -> Self {
258 Self(model)
259 }
260
261 pub fn create<P: AsRef<Path>>(file_name: P, version: AutosarVersion) -> Self {
266 let model = AutosarModel::new();
267 model.create_file(file_name, version).unwrap();
270 Self(model)
271 }
272
273 pub fn from_file<P: AsRef<Path>>(file_name: P) -> Result<Self, AutosarAbstractionError> {
275 let model = AutosarModel::new();
276 model.load_file(file_name, true)?;
277 Ok(Self(model))
278 }
279
280 pub fn from_buffer<P: AsRef<Path>>(
285 buffer: &[u8],
286 file_name: P,
287 strict: bool,
288 ) -> Result<Self, AutosarAbstractionError> {
289 let model = AutosarModel::new();
290 model.load_buffer(buffer, file_name, strict)?;
291 Ok(Self(model))
292 }
293
294 #[must_use]
296 pub fn model(&self) -> &AutosarModel {
297 &self.0
298 }
299
300 #[must_use]
302 pub fn root_element(&self) -> Element {
303 self.0.root_element()
304 }
305
306 pub fn packages(&self) -> impl Iterator<Item = ArPackage> + Send + use<> {
308 self.0
309 .root_element()
310 .get_sub_element(ElementName::ArPackages)
311 .into_iter()
312 .flat_map(|elem| elem.sub_elements())
313 .filter_map(|elem| ArPackage::try_from(elem).ok())
314 }
315
316 pub fn get_or_create_package(&self, path: &str) -> Result<ArPackage, AutosarAbstractionError> {
318 ArPackage::get_or_create(&self.0, path)
319 }
320
321 pub fn create_file(&self, file_name: &str, version: AutosarVersion) -> Result<ArxmlFile, AutosarAbstractionError> {
323 let arxml_file = self.0.create_file(file_name, version)?;
324 Ok(arxml_file)
325 }
326
327 pub fn load_file<P: AsRef<Path>>(
329 &self,
330 file_name: P,
331 strict: bool,
332 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarAbstractionError> {
333 let value = self.0.load_file(file_name, strict)?;
334 Ok(value)
335 }
336
337 pub fn load_buffer<P: AsRef<Path>>(
339 &self,
340 buffer: &[u8],
341 file_name: P,
342 strict: bool,
343 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarAbstractionError> {
344 let value = self.0.load_buffer(buffer, file_name, strict)?;
345 Ok(value)
346 }
347
348 pub fn files(&self) -> impl Iterator<Item = ArxmlFile> + Send + use<> {
350 self.0.files()
351 }
352
353 pub fn write(&self) -> Result<(), AutosarAbstractionError> {
355 self.0.write()?;
356 Ok(())
357 }
358
359 #[must_use]
361 pub fn get_element_by_path(&self, path: &str) -> Option<Element> {
362 self.0.get_element_by_path(path)
363 }
364
365 #[must_use]
382 pub fn find_system(&self) -> Option<System> {
383 System::find(&self.0)
384 }
385}
386
387#[derive(Debug, Clone, Copy, PartialEq, Eq)]
391pub enum ByteOrder {
392 MostSignificantByteFirst,
394 MostSignificantByteLast,
396 Opaque,
398}
399
400impl TryFrom<EnumItem> for ByteOrder {
401 type Error = AutosarAbstractionError;
402
403 fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
404 match value {
405 EnumItem::MostSignificantByteFirst => Ok(ByteOrder::MostSignificantByteFirst),
406 EnumItem::MostSignificantByteLast => Ok(ByteOrder::MostSignificantByteLast),
407 EnumItem::Opaque => Ok(ByteOrder::Opaque),
408 _ => Err(AutosarAbstractionError::ValueConversionError {
409 value: value.to_string(),
410 dest: "ByteOrder".to_string(),
411 }),
412 }
413 }
414}
415
416impl From<ByteOrder> for EnumItem {
417 fn from(value: ByteOrder) -> Self {
418 match value {
419 ByteOrder::MostSignificantByteFirst => EnumItem::MostSignificantByteFirst,
420 ByteOrder::MostSignificantByteLast => EnumItem::MostSignificantByteLast,
421 ByteOrder::Opaque => EnumItem::Opaque,
422 }
423 }
424}
425
426pub(crate) fn make_unique_name(model: &AutosarModel, base_path: &str, initial_name: &str) -> String {
429 let mut full_path = format!("{base_path}/{initial_name}");
430 let mut name = initial_name.to_string();
431 let mut counter = 0;
432 while model.get_element_by_path(&full_path).is_some() {
433 counter += 1;
434 name = format!("{initial_name}_{counter}");
435 full_path = format!("{base_path}/{name}");
436 }
437
438 name
439}
440
441pub(crate) fn is_used(element: &Element) -> bool {
445 let Ok(model) = element.model() else {
446 return false;
448 };
449 let Ok(path) = element.path() else {
450 return false;
453 };
454 let references = model.get_references_to(&path);
455
456 !references.is_empty()
458}
459
460pub(crate) fn get_reference_parents(element: &Element) -> Result<Vec<(Element, Element)>, AutosarAbstractionError> {
464 let model = element.model()?;
465 let path = element.path()?;
466 let references = model.get_references_to(&path);
467
468 let parents = references
469 .iter()
470 .filter_map(WeakElement::upgrade)
471 .filter_map(|ref_elem| {
472 Some((
473 ref_elem.named_parent().ok().flatten()?,
474 ref_elem.parent().ok().flatten()?,
475 ))
476 })
477 .collect();
478
479 Ok(parents)
480}
481
482#[cfg(test)]
485mod test {
486 use super::*;
487 use autosar_data::AutosarModel;
488
489 #[test]
490 fn create_model() {
491 let raw_model = AutosarModel::new();
493 let model = AutosarModelAbstraction::new(raw_model.clone());
494 assert_eq!(model.model(), &raw_model);
495
496 let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00049);
498 let root = model.root_element();
499 assert_eq!(root.element_name(), ElementName::Autosar);
500 }
501
502 #[test]
503 fn create_model_from_file() {
504 let tempdir = tempfile::tempdir().unwrap();
505 let filename = tempdir.path().join("test.arxml");
506
507 let model1 = AutosarModelAbstraction::create(filename.clone(), AutosarVersion::LATEST);
509 model1.write().unwrap();
510
511 let model2 = AutosarModelAbstraction::from_file(filename).unwrap();
513 let root = model2.root_element();
514 assert_eq!(root.element_name(), ElementName::Autosar);
515 }
516
517 #[test]
518 fn model_files() {
519 let model = AutosarModelAbstraction::create("file1.arxml", AutosarVersion::Autosar_00049);
520 let file = model.create_file("file2.arxml", AutosarVersion::Autosar_00049).unwrap();
521 let files: Vec<_> = model.files().collect();
522 assert_eq!(files.len(), 2);
523 assert_eq!(files[1], file);
524 }
525
526 #[test]
527 fn model_load_file() {
528 let tempdir = tempfile::tempdir().unwrap();
529 let filename = tempdir.path().join("test.arxml");
530
531 let model = AutosarModelAbstraction::create(filename.clone(), AutosarVersion::LATEST);
533 model.write().unwrap();
534
535 let model = AutosarModelAbstraction::new(AutosarModel::new());
537 let (_file, errors) = model.load_file(filename, true).unwrap();
538 assert!(errors.is_empty());
539 }
540
541 #[test]
542 fn model_packages() {
543 let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00049);
544 let package = model.get_or_create_package("/package").unwrap();
545 let package2 = model.get_or_create_package("/other_package").unwrap();
546 model.get_or_create_package("/other_package/sub_package").unwrap();
547 let packages: Vec<_> = model.packages().collect();
548 assert_eq!(packages.len(), 2);
549 assert_eq!(packages[0], package);
550 assert_eq!(packages[1], package2);
551 }
552
553 #[test]
554 fn errors() {
555 let model = AutosarModel::new();
556
557 let err = AutosarAbstractionError::ConversionError {
558 element: model.root_element(),
559 dest: "TEST".to_string(),
560 };
561 let string = format!("{err}");
562 assert!(!string.is_empty());
563
564 let err = AutosarAbstractionError::InvalidPath("lorem ipsum".to_string());
565 let string = format!("{err}");
566 assert!(!string.is_empty());
567
568 let err = AutosarAbstractionError::ItemAlreadyExists;
569 let string = format!("{err}");
570 assert!(!string.is_empty());
571 }
572
573 #[test]
574 fn from_buffer() {
575 let buffer = br#"
576 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
577 <AUTOSAR xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00046.xsd">
578 <AR-PACKAGES>
579 <AR-PACKAGE>
580 <SHORT-NAME>MyPackage</SHORT-NAME>
581 </AR-PACKAGE>
582 </AR-PACKAGES>
583 </AUTOSAR>
584 "#;
585
586 let model = AutosarModelAbstraction::from_buffer(buffer, "buffer.arxml", true).unwrap();
587 let package = model.get_or_create_package("/MyPackage").unwrap();
588 assert_eq!(package.name().unwrap(), "MyPackage");
589 }
590
591 #[test]
592 fn load_buffer() {
593 let model = AutosarModelAbstraction::create("dummy", AutosarVersion::LATEST);
594 let buffer = br#"
595 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
596 <AUTOSAR xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00046.xsd">
597 <AR-PACKAGES>
598 <AR-PACKAGE>
599 <SHORT-NAME>MyPackage</SHORT-NAME>
600 </AR-PACKAGE>
601 </AR-PACKAGES>
602 </AUTOSAR>
603 "#;
604 let (_file, errors) = model.load_buffer(buffer, "buffer.arxml", true).unwrap();
605 assert!(errors.is_empty());
606 assert_eq!(model.files().count(), 2);
607 }
608}