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;
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 #[must_use]
282 pub fn model(&self) -> &AutosarModel {
283 &self.0
284 }
285
286 #[must_use]
288 pub fn root_element(&self) -> Element {
289 self.0.root_element()
290 }
291
292 pub fn packages(&self) -> impl Iterator<Item = ArPackage> + Send + use<> {
294 self.0
295 .root_element()
296 .get_sub_element(ElementName::ArPackages)
297 .into_iter()
298 .flat_map(|elem| elem.sub_elements())
299 .filter_map(|elem| ArPackage::try_from(elem).ok())
300 }
301
302 pub fn get_or_create_package(&self, path: &str) -> Result<ArPackage, AutosarAbstractionError> {
304 ArPackage::get_or_create(&self.0, path)
305 }
306
307 pub fn create_file(&self, file_name: &str, version: AutosarVersion) -> Result<ArxmlFile, AutosarAbstractionError> {
309 let arxml_file = self.0.create_file(file_name, version)?;
310 Ok(arxml_file)
311 }
312
313 pub fn load_file<P: AsRef<Path>>(
315 &self,
316 file_name: P,
317 strict: bool,
318 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarAbstractionError> {
319 let value = self.0.load_file(file_name, strict)?;
320 Ok(value)
321 }
322
323 pub fn files(&self) -> impl Iterator<Item = ArxmlFile> + Send + use<> {
325 self.0.files()
326 }
327
328 pub fn write(&self) -> Result<(), AutosarAbstractionError> {
330 self.0.write()?;
331 Ok(())
332 }
333
334 #[must_use]
336 pub fn get_element_by_path(&self, path: &str) -> Option<Element> {
337 self.0.get_element_by_path(path)
338 }
339
340 #[must_use]
357 pub fn find_system(&self) -> Option<System> {
358 System::find(&self.0)
359 }
360}
361
362#[derive(Debug, Clone, Copy, PartialEq, Eq)]
366pub enum ByteOrder {
367 MostSignificantByteFirst,
369 MostSignificantByteLast,
371 Opaque,
373}
374
375impl TryFrom<EnumItem> for ByteOrder {
376 type Error = AutosarAbstractionError;
377
378 fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
379 match value {
380 EnumItem::MostSignificantByteFirst => Ok(ByteOrder::MostSignificantByteFirst),
381 EnumItem::MostSignificantByteLast => Ok(ByteOrder::MostSignificantByteLast),
382 EnumItem::Opaque => Ok(ByteOrder::Opaque),
383 _ => Err(AutosarAbstractionError::ValueConversionError {
384 value: value.to_string(),
385 dest: "ByteOrder".to_string(),
386 }),
387 }
388 }
389}
390
391impl From<ByteOrder> for EnumItem {
392 fn from(value: ByteOrder) -> Self {
393 match value {
394 ByteOrder::MostSignificantByteFirst => EnumItem::MostSignificantByteFirst,
395 ByteOrder::MostSignificantByteLast => EnumItem::MostSignificantByteLast,
396 ByteOrder::Opaque => EnumItem::Opaque,
397 }
398 }
399}
400
401pub(crate) fn make_unique_name(model: &AutosarModel, base_path: &str, initial_name: &str) -> String {
404 let mut full_path = format!("{base_path}/{initial_name}");
405 let mut name = initial_name.to_string();
406 let mut counter = 0;
407 while model.get_element_by_path(&full_path).is_some() {
408 counter += 1;
409 name = format!("{initial_name}_{counter}");
410 full_path = format!("{base_path}/{name}");
411 }
412
413 name
414}
415
416pub(crate) fn is_used(element: &Element) -> bool {
420 let Ok(model) = element.model() else {
421 return false;
423 };
424 let Ok(path) = element.path() else {
425 return false;
428 };
429 let references = model.get_references_to(&path);
430
431 !references.is_empty()
433}
434
435pub(crate) fn get_reference_parents(element: &Element) -> Result<Vec<(Element, Element)>, AutosarAbstractionError> {
439 let model = element.model()?;
440 let path = element.path()?;
441 let references = model.get_references_to(&path);
442
443 let parents = references
444 .iter()
445 .filter_map(WeakElement::upgrade)
446 .filter_map(|ref_elem| {
447 Some((
448 ref_elem.named_parent().ok().flatten()?,
449 ref_elem.parent().ok().flatten()?,
450 ))
451 })
452 .collect();
453
454 Ok(parents)
455}
456
457#[cfg(test)]
460mod test {
461 use super::*;
462 use autosar_data::AutosarModel;
463
464 #[test]
465 fn create_model() {
466 let raw_model = AutosarModel::new();
468 let model = AutosarModelAbstraction::new(raw_model.clone());
469 assert_eq!(model.model(), &raw_model);
470
471 let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00049);
473 let root = model.root_element();
474 assert_eq!(root.element_name(), ElementName::Autosar);
475 }
476
477 #[test]
478 fn create_model_from_file() {
479 let tempdir = tempfile::tempdir().unwrap();
480 let filename = tempdir.path().join("test.arxml");
481
482 let model1 = AutosarModelAbstraction::create(filename.clone(), AutosarVersion::LATEST);
484 model1.write().unwrap();
485
486 let model2 = AutosarModelAbstraction::from_file(filename).unwrap();
488 let root = model2.root_element();
489 assert_eq!(root.element_name(), ElementName::Autosar);
490 }
491
492 #[test]
493 fn model_files() {
494 let model = AutosarModelAbstraction::create("file1.arxml", AutosarVersion::Autosar_00049);
495 let file = model.create_file("file2.arxml", AutosarVersion::Autosar_00049).unwrap();
496 let files: Vec<_> = model.files().collect();
497 assert_eq!(files.len(), 2);
498 assert_eq!(files[1], file);
499 }
500
501 #[test]
502 fn model_load_file() {
503 let tempdir = tempfile::tempdir().unwrap();
504 let filename = tempdir.path().join("test.arxml");
505
506 let model = AutosarModelAbstraction::create(filename.clone(), AutosarVersion::LATEST);
508 model.write().unwrap();
509
510 let model = AutosarModelAbstraction::new(AutosarModel::new());
512 let (_file, errors) = model.load_file(filename, true).unwrap();
513 assert!(errors.is_empty());
514 }
515
516 #[test]
517 fn model_packages() {
518 let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00049);
519 let package = model.get_or_create_package("/package").unwrap();
520 let package2 = model.get_or_create_package("/other_package").unwrap();
521 model.get_or_create_package("/other_package/sub_package").unwrap();
522 let packages: Vec<_> = model.packages().collect();
523 assert_eq!(packages.len(), 2);
524 assert_eq!(packages[0], package);
525 assert_eq!(packages[1], package2);
526 }
527
528 #[test]
529 fn errors() {
530 let model = AutosarModel::new();
531
532 let err = AutosarAbstractionError::ConversionError {
533 element: model.root_element(),
534 dest: "TEST".to_string(),
535 };
536 let string = format!("{err}");
537 assert!(!string.is_empty());
538
539 let err = AutosarAbstractionError::InvalidPath("lorem ipsum".to_string());
540 let string = format!("{err}");
541 assert!(!string.is_empty());
542
543 let err = AutosarAbstractionError::ItemAlreadyExists;
544 let string = format!("{err}");
545 assert!(!string.is_empty());
546 }
547}