1use std::{collections::HashMap, hash::Hash};
2
3use crate::*;
4
5#[derive(Debug)]
6enum MergeAction {
8 MergeEqual,
9 MergeUnequal(Element),
10 AOnly,
11 BOnly(usize),
12}
13
14impl AutosarModel {
15 #[must_use]
27 pub fn new() -> AutosarModel {
28 let version = AutosarVersion::LATEST;
29 let xsi_schemalocation =
30 CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", version.filename()));
31 let xmlns = CharacterData::String("http://autosar.org/schema/r4.0".to_string());
32 let xmlns_xsi = CharacterData::String("http://www.w3.org/2001/XMLSchema-instance".to_string());
33 let root_attributes = smallvec::smallvec![
34 Attribute {
35 attrname: AttributeName::xsiSchemalocation,
36 content: xsi_schemalocation
37 },
38 Attribute {
39 attrname: AttributeName::xmlns,
40 content: xmlns
41 },
42 Attribute {
43 attrname: AttributeName::xmlnsXsi,
44 content: xmlns_xsi
45 },
46 ];
47 let root_elem = ElementRaw {
48 parent: ElementOrModel::None,
49 elemname: ElementName::Autosar,
50 elemtype: ElementType::ROOT,
51 content: SmallVec::new(),
52 attributes: root_attributes,
53 file_membership: HashSet::with_capacity(0),
54 comment: None,
55 }
56 .wrap();
57 let model = AutosarModelRaw {
58 files: Vec::new(),
59 identifiables: FxIndexMap::default(),
60 reference_origins: FxHashMap::default(),
61 root_element: root_elem.clone(),
62 }
63 .wrap();
64 root_elem.set_parent(ElementOrModel::Model(model.downgrade()));
65 model
66 }
67
68 pub fn create_file<P: AsRef<Path>>(
96 &self,
97 filename: P,
98 version: AutosarVersion,
99 ) -> Result<ArxmlFile, AutosarDataError> {
100 let mut data = self.0.write();
101
102 if data.files.iter().any(|af| af.filename() == filename.as_ref()) {
103 return Err(AutosarDataError::DuplicateFilenameError {
104 verb: "create",
105 filename: filename.as_ref().to_path_buf(),
106 });
107 }
108
109 let new_file = ArxmlFile::new(filename, version, self);
110
111 data.files.push(new_file.clone());
112
113 let _ = data.root_element.add_to_file_restricted(&new_file);
115
116 Ok(new_file)
117 }
118
119 pub fn load_buffer<P: AsRef<Path>>(
150 &self,
151 buffer: &[u8],
152 filename: P,
153 strict: bool,
154 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
155 self.load_buffer_internal(buffer, filename.as_ref().to_path_buf(), strict)
156 }
157
158 fn load_buffer_internal(
159 &self,
160 buffer: &[u8],
161 filename: PathBuf,
162 strict: bool,
163 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
164 if self.files().any(|file| file.filename() == filename) {
165 return Err(AutosarDataError::DuplicateFilenameError { verb: "load", filename });
166 }
167
168 let mut parser = ArxmlParser::new(filename.clone(), buffer, strict);
169 let root_element = parser.parse_arxml()?;
170 let version = parser.get_fileversion();
171 let arxml_file = ArxmlFileRaw {
172 version,
173 model: self.downgrade(),
174 filename: filename.clone(),
175 xml_standalone: parser.get_standalone(),
176 }
177 .wrap();
178
179 if self.0.read().files.is_empty() {
180 root_element.set_parent(ElementOrModel::Model(self.downgrade()));
181 root_element.0.write().file_membership.insert(arxml_file.downgrade());
182 self.0.write().root_element = root_element;
183 } else {
184 let result = self.merge_file_data(&root_element, arxml_file.downgrade());
185 if let Err(error) = result {
186 let _ = self.root_element().remove_from_file(&arxml_file);
187 return Err(error);
188 }
189 }
190
191 let mut data = self.0.write();
192 data.identifiables.reserve(parser.identifiables.len());
193 for (key, value) in parser.identifiables {
194 if let Some(existing_element) = data.identifiables.get(&key).and_then(WeakElement::upgrade) {
197 if let Some(new_element) = value.upgrade()
199 && existing_element.element_name() != new_element.element_name()
200 {
201 return Err(AutosarDataError::OverlappingDataError {
203 filename,
204 path: new_element.xml_path(),
205 });
206 }
207 } else {
208 data.identifiables.insert(key, value);
209 }
210 }
211 data.reference_origins.reserve(parser.references.len());
212 for (refpath, referring_element) in parser.references {
213 if let Some(xref) = data.reference_origins.get_mut(&refpath) {
214 xref.push(referring_element);
215 } else {
216 data.reference_origins.insert(refpath, vec![referring_element]);
217 }
218 }
219 data.files.push(arxml_file.clone());
220
221 Ok((arxml_file, parser.warnings))
222 }
223
224 fn merge_file_data(&self, new_root: &Element, new_file: WeakArxmlFile) -> Result<(), AutosarDataError> {
233 let root = self.root_element();
234 let files: HashSet<WeakArxmlFile> = self.files().map(|f| f.downgrade()).collect();
235
236 Self::merge_element(&root, &files, new_root, &new_file).map_err(|e| {
237 if let AutosarDataError::ElementInsertionConflict { parent_path, .. } = &e {
239 AutosarDataError::InvalidFileMerge {
240 path: parent_path.clone(),
241 }
242 } else {
243 e
244 }
245 })?;
246
247 self.root_element().0.write().file_membership.insert(new_file);
248
249 Ok(())
250 }
251
252 fn merge_element(
253 parent_a: &Element,
254 files: &HashSet<WeakArxmlFile>,
255 parent_b: &Element,
256 new_file: &WeakArxmlFile,
257 ) -> Result<(), AutosarDataError> {
258 let mut iter_a = parent_a.sub_elements().enumerate();
259 let mut iter_b = parent_b.sub_elements();
260 let mut item_a = iter_a.next();
261 let mut item_b = iter_b.next();
262 let mut elements_a_only = Vec::<Element>::new();
263 let mut elements_b_only = Vec::<(Element, usize)>::new();
264 let mut elements_merge = Vec::<(Element, Element)>::new();
265 let min_ver_a = files
266 .iter()
267 .filter_map(|weak| weak.upgrade().map(|f| f.version()))
268 .min()
269 .unwrap_or(AutosarVersion::LATEST);
270 let min_ver_b = new_file.upgrade().map_or(AutosarVersion::LATEST, |f| f.version());
271 let version = std::cmp::min(min_ver_a, min_ver_b);
272 let splitable = parent_a.element_type().splittable_in(version);
273
274 while let (Some((pos_a, elem_a)), Some(elem_b)) = (&item_a, &item_b) {
275 let merge_action = if elem_a.element_name() == elem_b.element_name() {
276 if elem_a.is_identifiable() {
277 Self::calc_identifiables_merge(parent_a, parent_b, elem_a, elem_b, splitable)?
278 } else {
279 Self::calc_element_merge(parent_b, elem_a, elem_b)
280 }
281 } else {
282 let parent_type = parent_a.element_type();
284 let (_, indices_a) = parent_type.find_sub_element(elem_a.element_name(), u32::MAX).unwrap();
290 let (_, indices_b) = parent_type.find_sub_element(elem_b.element_name(), u32::MAX).unwrap();
291 if indices_a < indices_b {
292 MergeAction::AOnly
296 } else {
297 MergeAction::BOnly(*pos_a)
301 }
302 };
303
304 match merge_action {
305 MergeAction::MergeEqual => {
306 elements_merge.push((elem_a.clone(), elem_b.clone()));
307 item_a = iter_a.next();
308 item_b = iter_b.next();
309 }
310 MergeAction::MergeUnequal(other_b) => {
311 elements_merge.push((elem_a.clone(), other_b));
312 item_a = iter_a.next();
313 }
314 MergeAction::AOnly => {
315 elements_a_only.push(elem_a.clone());
316 item_a = iter_a.next();
317 }
318 MergeAction::BOnly(position) => {
319 if !elements_merge.iter().any(|(_, merge_b)| merge_b == elem_b) {
320 elements_b_only.push((elem_b.clone(), position));
321 }
322 item_b = iter_b.next();
323 }
324 }
325 }
326 if let Some((_, elem_a)) = item_a {
329 elements_a_only.push(elem_a);
330 for (_, elem_a) in iter_a {
331 elements_a_only.push(elem_a);
332 }
333 }
334 if let Some(elem_b) = item_b {
335 let elem_count = parent_a.0.read().content.len();
336 if !elements_merge.iter().any(|(_, merge_b)| merge_b == &elem_b) {
337 elements_b_only.push((elem_b, elem_count));
338 }
339 for elem_b in iter_b {
340 if !elements_merge.iter().any(|(_, merge_b)| merge_b == &elem_b) {
341 elements_b_only.push((elem_b, elem_count));
342 }
343 }
344 }
345
346 for element in elements_a_only {
348 let mut elem_locked = element.0.write();
350 if elem_locked.file_membership.is_empty() {
351 files.clone_into(&mut elem_locked.file_membership);
352 }
353 }
354
355 Self::import_new_items(parent_a, elements_b_only, new_file, min_ver_b)?;
358
359 Self::merge_sub_elements(parent_a, elements_merge, files, new_file, version)?;
361
362 Ok(())
363 }
364
365 fn calc_identifiables_merge(
368 parent_a: &Element,
369 parent_b: &Element,
370 elem_a: &Element,
371 elem_b: &Element,
372 splitable: bool,
373 ) -> Result<MergeAction, AutosarDataError> {
374 Ok(if elem_a.item_name() == elem_b.item_name() {
375 MergeAction::MergeEqual
378 } else {
379 if let Some(sibling) = parent_b
382 .sub_elements()
383 .find(|e| e.element_name() == elem_a.element_name() && e.item_name() == elem_a.item_name())
384 {
385 MergeAction::MergeUnequal(sibling)
387 } else {
388 if splitable {
390 MergeAction::AOnly
391 } else {
392 return Err(AutosarDataError::InvalidFileMerge {
393 path: parent_a.xml_path(),
394 });
395 }
396 }
397 })
398 }
399
400 fn calc_element_merge(parent_b: &Element, elem_a: &Element, elem_b: &Element) -> MergeAction {
403 let defref_a = elem_a
405 .get_sub_element(ElementName::DefinitionRef)
406 .and_then(|dr| dr.character_data())
407 .and_then(|cdata| cdata.string_value());
408 let defref_b = elem_b
409 .get_sub_element(ElementName::DefinitionRef)
410 .and_then(|dr| dr.character_data())
411 .and_then(|cdata| cdata.string_value());
412 if defref_a == defref_b {
415 if elem_a.character_data() != elem_b.character_data() {
417 MergeAction::AOnly
420 } else {
421 MergeAction::MergeEqual
424 }
425 } else {
426 if let Some(sibling) = parent_b
429 .sub_elements()
430 .filter(|e| e.element_name() == elem_a.element_name())
431 .find(|e| {
432 e.get_sub_element(ElementName::DefinitionRef)
433 .and_then(|dr| dr.character_data())
434 .and_then(|cdata| cdata.string_value())
435 == defref_a
436 })
437 {
438 MergeAction::MergeUnequal(sibling)
440 } else {
441 MergeAction::AOnly
444 }
445 }
446 }
447
448 fn import_new_items(
449 parent_a: &Element,
450 elements_b_only: Vec<(Element, usize)>,
451 new_file: &WeakArxmlFile,
452 version: AutosarVersion,
453 ) -> Result<(), AutosarDataError> {
454 for (idx, (new_element, insert_pos)) in elements_b_only.into_iter().enumerate() {
456 let dest = insert_pos + idx;
458
459 Self::import_single_item(parent_a, new_element, dest, new_file, version)?;
460 }
461 Ok(())
462 }
463
464 fn import_single_item(
465 parent_a: &Element,
466 new_element: Element,
467 dest: usize,
468 new_file: &WeakArxmlFile,
469 version: AutosarVersion,
470 ) -> Result<(), AutosarDataError> {
471 let mut parent_a_locked = parent_a.0.write();
472 let weak_parent_a = parent_a.downgrade();
473
474 new_element.set_parent(ElementOrModel::Element(weak_parent_a));
475 new_element.0.write().file_membership.insert(new_file.clone());
477
478 let (first_pos, last_pos) = parent_a_locked.calc_element_insert_range(new_element.element_name(), version)?;
481
482 let dest = dest.max(first_pos).min(last_pos);
484
485 parent_a_locked
487 .content
488 .insert(dest, ElementContent::Element(new_element));
489
490 Ok(())
491 }
492
493 fn merge_sub_elements(
494 parent_a: &Element,
495 elements_merge: Vec<(Element, Element)>,
496 files: &HashSet<WeakArxmlFile>,
497 new_file: &WeakArxmlFile,
498 version: AutosarVersion,
499 ) -> Result<(), AutosarDataError> {
500 for (elem_a, elem_b) in elements_merge {
501 let files = if !elem_a.0.read().file_membership.is_empty() {
503 elem_a.0.read().file_membership.clone()
504 } else {
505 files.clone()
506 };
507
508 let result = AutosarModel::merge_element(&elem_a, &files, &elem_b, new_file);
510 match result {
511 Ok(()) => {
512 let mut elem_a_locked = elem_a.0.write();
514 if !elem_a_locked.file_membership.is_empty() {
515 elem_a_locked.file_membership.insert(new_file.clone());
516 }
517 }
518 Err(e) => {
519 if let AutosarDataError::ElementInsertionConflict { parent_path, .. } = &e {
520 if elem_a.is_identifiable() {
522 return Err(AutosarDataError::InvalidFileMerge {
524 path: parent_path.clone(),
525 });
526 } else if parent_a.element_type().splittable_in(version) {
527 elem_a.set_file_membership(files);
528 let dest = elem_a.position().unwrap_or_default() + 1;
530 return Self::import_single_item(parent_a, elem_b, dest, new_file, version).map_err(|_| e);
531 }
532 }
533
534 return Err(e);
536 }
537 }
538 }
539 Ok(())
540 }
541
542 pub fn load_file<P: AsRef<Path>>(
570 &self,
571 filename: P,
572 strict: bool,
573 ) -> Result<(ArxmlFile, Vec<AutosarDataError>), AutosarDataError> {
574 let filename_buf = filename.as_ref().to_path_buf();
575 let buffer = std::fs::read(&filename_buf).map_err(|err| AutosarDataError::IoErrorRead {
576 filename: filename_buf.clone(),
577 ioerror: err,
578 })?;
579
580 self.load_buffer(&buffer, &filename_buf, strict)
581 }
582
583 pub fn remove_file(&self, file: &ArxmlFile) {
601 let mut locked_model = self.0.write();
602 let find_result = locked_model
603 .files
604 .iter()
605 .enumerate()
606 .find(|(_, f)| *f == file)
607 .map(|(pos, _)| pos);
608 if let Some(pos) = find_result {
610 locked_model.files.swap_remove(pos);
611 if locked_model.files.is_empty() {
612 locked_model.root_element.0.write().content.clear();
614 locked_model.root_element.set_file_membership(HashSet::new());
615 locked_model.identifiables.clear();
616 locked_model.reference_origins.clear();
617 } else {
618 drop(locked_model);
619 let _ = self.root_element().remove_from_file(file);
621 }
623 }
624 }
625
626 #[must_use]
644 pub fn serialize_files(&self) -> HashMap<PathBuf, String> {
645 let mut result = HashMap::new();
646 for file in self.files() {
647 if let Ok(data) = file.serialize() {
648 result.insert(file.filename(), data);
649 }
650 }
651 result
652 }
653
654 pub fn write(&self) -> Result<(), AutosarDataError> {
677 for (pathbuf, filedata) in self.serialize_files() {
678 std::fs::write(pathbuf.clone(), filedata).map_err(|err| AutosarDataError::IoErrorWrite {
679 filename: pathbuf,
680 ioerror: err,
681 })?;
682 }
683 Ok(())
684 }
685
686 #[must_use]
702 pub fn files(&self) -> ArxmlFileIterator {
703 ArxmlFileIterator::new(self.clone())
704 }
705
706 #[must_use]
717 pub fn root_element(&self) -> Element {
718 let locked_model = self.0.read();
719 locked_model.root_element.clone()
720 }
721
722 #[must_use]
744 pub fn get_element_by_path(&self, path: &str) -> Option<Element> {
745 let model = self.0.read();
746 model.identifiables.get(path).and_then(WeakElement::upgrade)
747 }
748
749 pub fn duplicate(&self) -> Result<AutosarModel, AutosarDataError> {
776 let copy = Self::new();
777 let mut filemap = HashMap::new();
778
779 for orig_file in self.files() {
780 let filename = orig_file.filename();
781 let new_file = copy.create_file(filename.clone(), orig_file.version())?;
782 new_file.0.write().xml_standalone = orig_file.0.read().xml_standalone;
783 filemap.insert(filename, new_file.downgrade());
784 }
785
786 for element in self.root_element().sub_elements() {
789 copy.root_element().create_copied_sub_element(&element)?;
790 }
791
792 let orig_iter = self.elements_dfs();
795 let copy_iter = copy.elements_dfs();
796 let combined = std::iter::zip(orig_iter, copy_iter);
797 for ((_, orig_elem), (_, copy_elem)) in combined {
798 let mut locked_copy = copy_elem.0.try_write().ok_or(AutosarDataError::ParentElementLocked)?;
799 locked_copy.file_membership.clear();
800
801 for orig_file in orig_elem.0.read().file_membership.iter().filter_map(|w| w.upgrade()) {
802 if let Some(copy_file) = filemap.get(&orig_file.filename()) {
803 locked_copy.file_membership.insert(copy_file.clone());
804 }
805 }
806 }
807
808 Ok(copy)
809 }
810
811 #[must_use]
839 pub fn elements_dfs(&self) -> ElementsDfsIterator {
840 self.root_element().elements_dfs()
841 }
842
843 #[must_use]
864 pub fn elements_dfs_with_max_depth(&self, max_depth: usize) -> ElementsDfsIterator {
865 self.root_element().elements_dfs_with_max_depth(max_depth)
866 }
867
868 pub fn sort(&self) {
885 self.root_element().sort();
886 }
887
888 #[must_use]
906 pub fn identifiable_elements(&self) -> IdentifiablesIterator {
907 IdentifiablesIterator::new(self)
908 }
909
910 #[must_use]
933 pub fn get_references_to(&self, target_path: &str) -> Vec<WeakElement> {
934 if let Some(origins) = self.0.read().reference_origins.get(target_path) {
935 origins.clone()
936 } else {
937 Vec::new()
938 }
939 }
940
941 #[must_use]
961 pub fn check_references(&self) -> Vec<WeakElement> {
962 let mut broken_refs = Vec::new();
963
964 let model = self.0.read();
965 for (path, element_list) in &model.reference_origins {
966 if let Some(target_elem_weak) = model.identifiables.get(path) {
967 if let Some(target_elem) = target_elem_weak.upgrade() {
969 for referring_elem_weak in element_list {
972 if let Some(referring_elem) = referring_elem_weak.upgrade() {
973 if let Some(CharacterData::Enum(dest_value)) =
974 referring_elem.attribute_value(AttributeName::Dest)
975 {
976 if !target_elem.element_type().verify_reference_dest(dest_value) {
977 broken_refs.push(referring_elem_weak.clone());
979 }
980 } else {
981 broken_refs.push(referring_elem_weak.clone());
983 }
984 }
985 }
986 } else {
987 broken_refs.extend(element_list.iter().cloned());
991 }
992 } else {
993 broken_refs.extend(element_list.iter().cloned());
995 }
996 }
997
998 broken_refs
999 }
1000
1001 pub(crate) fn downgrade(&self) -> WeakAutosarModel {
1003 WeakAutosarModel(Arc::downgrade(&self.0))
1004 }
1005
1006 pub(crate) fn add_identifiable(&self, new_path: String, elem: WeakElement) {
1008 let mut model = self.0.write();
1009 model.identifiables.insert(new_path, elem);
1010 }
1011
1012 pub(crate) fn fix_identifiables(&self, old_path: &str, new_path: &str) {
1014 let mut model = self.0.write();
1015
1016 let keys: Vec<String> = model.identifiables.keys().cloned().collect();
1018 for key in keys {
1019 if let Some(suffix) = key.strip_prefix(old_path)
1021 && (suffix.is_empty() || suffix.starts_with('/'))
1022 {
1023 let new_key = format!("{new_path}{suffix}");
1024 if let Some(entry) = model.identifiables.swap_remove(&key) {
1026 model.identifiables.insert(new_key, entry);
1027 }
1028 }
1029 }
1030 }
1031
1032 pub(crate) fn remove_identifiable(&self, path: &str) {
1034 let mut model = self.0.write();
1035 model.identifiables.swap_remove(path);
1036 }
1037
1038 pub(crate) fn add_reference_origin(&self, new_ref: &str, origin: WeakElement) {
1039 let mut data = self.0.write();
1040 if let Some(referrer_list) = data.reference_origins.get_mut(new_ref) {
1042 referrer_list.push(origin);
1043 } else {
1044 data.reference_origins.insert(new_ref.to_owned(), vec![origin]);
1045 }
1046 }
1047
1048 pub(crate) fn fix_reference_origins(&self, old_ref: &str, new_ref: &str, origin: WeakElement) {
1049 if old_ref != new_ref {
1050 let mut data = self.0.write();
1051 let mut remove_list = false;
1052 if let Some(referrer_list) = data.reference_origins.get_mut(old_ref)
1054 && let Some(index) = referrer_list.iter().position(|x| *x == origin)
1055 {
1056 referrer_list.swap_remove(index);
1057 remove_list = referrer_list.is_empty();
1058 }
1059 if remove_list {
1060 data.reference_origins.remove(old_ref);
1061 }
1062 if let Some(referrer_list) = data.reference_origins.get_mut(new_ref) {
1064 referrer_list.push(origin);
1065 } else {
1066 data.reference_origins.insert(new_ref.to_owned(), vec![origin]);
1067 }
1068 }
1069 }
1070
1071 pub(crate) fn remove_reference_origin(&self, reference: &str, element: WeakElement) {
1072 let mut data = self.0.write();
1073 let mut count = 1;
1074 if let Some(referrer_list) = data.reference_origins.get_mut(reference) {
1075 if let Some(index) = referrer_list.iter().position(|x| *x == element) {
1076 referrer_list.swap_remove(index);
1077 }
1078 count = referrer_list.len();
1079 }
1080 if count == 0 {
1081 data.reference_origins.remove(reference);
1082 }
1083 }
1084}
1085
1086impl AutosarModelRaw {
1087 pub(crate) fn set_version(&mut self, new_ver: AutosarVersion) {
1088 let attribute_value = CharacterData::String(format!("http://autosar.org/schema/r4.0 {}", new_ver.filename()));
1089 let _ = self.root_element.0.write().set_attribute_internal(
1090 AttributeName::xsiSchemalocation,
1091 attribute_value,
1092 new_ver,
1093 );
1094 }
1095
1096 pub(crate) fn wrap(self) -> AutosarModel {
1097 AutosarModel(Arc::new(RwLock::new(self)))
1098 }
1099}
1100
1101impl std::fmt::Debug for AutosarModel {
1102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1103 let model = self.0.read();
1104 let rootelem = model.root_element.clone();
1107 let mut dbgstruct = f.debug_struct("AutosarModel");
1108 dbgstruct.field("root_element", &rootelem);
1109 dbgstruct.field("files", &model.files);
1110 dbgstruct.field("identifiables", &model.identifiables);
1111 dbgstruct.field("reference_origins", &model.reference_origins);
1112 dbgstruct.finish()
1113 }
1114}
1115
1116impl Default for AutosarModel {
1117 fn default() -> Self {
1118 Self::new()
1119 }
1120}
1121
1122impl PartialEq for AutosarModel {
1123 fn eq(&self, other: &Self) -> bool {
1124 Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0)
1125 }
1126}
1127
1128impl Eq for AutosarModel {}
1129
1130impl Hash for AutosarModel {
1131 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1132 state.write_usize(Arc::as_ptr(&self.0) as usize);
1133 }
1134}
1135
1136impl WeakAutosarModel {
1137 pub(crate) fn upgrade(&self) -> Option<AutosarModel> {
1138 Weak::upgrade(&self.0).map(AutosarModel)
1139 }
1140}
1141
1142impl std::fmt::Debug for WeakAutosarModel {
1143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1144 f.write_fmt(format_args!("AutosarModel:WeakRef {:p}", Weak::as_ptr(&self.0)))
1145 }
1146}
1147
1148#[cfg(test)]
1149mod test {
1150 use super::*;
1151 use tempfile::tempdir;
1152
1153 #[test]
1154 fn create_file() {
1155 let model = AutosarModel::new();
1156 let file = model.create_file("test", AutosarVersion::Autosar_00050);
1157 assert!(file.is_ok());
1158 let file = model.create_file("test", AutosarVersion::Autosar_00050);
1160 assert!(file.is_err());
1161 }
1162
1163 #[test]
1164 fn load_buffer() {
1165 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1166 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1167 <AR-PACKAGES>
1168 <AR-PACKAGE>
1169 <SHORT-NAME>Pkg</SHORT-NAME>
1170 <ELEMENTS>
1171 <SYSTEM><SHORT-NAME>Thing</SHORT-NAME></SYSTEM>
1172 </ELEMENTS>
1173 </AR-PACKAGE>
1174 </AR-PACKAGES></AUTOSAR>"#;
1175 const FILEBUF2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1176 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1177 <AR-PACKAGES>
1178 <AR-PACKAGE><SHORT-NAME>OtherPkg</SHORT-NAME></AR-PACKAGE>
1179 </AR-PACKAGES></AUTOSAR>"#;
1180 const FILEBUF3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1181 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1182 <AR-PACKAGES>
1183 <AR-PACKAGE>
1184 <SHORT-NAME>Pkg</SHORT-NAME>
1185 <ELEMENTS>
1186 <APPLICATION-PRIMITIVE-DATA-TYPE><SHORT-NAME>Thing</SHORT-NAME></APPLICATION-PRIMITIVE-DATA-TYPE>
1187 </ELEMENTS>
1188 </AR-PACKAGE>
1189 </AR-PACKAGES></AUTOSAR>"#;
1190 const NON_ARXML: &str = "The quick brown fox jumps over the lazy dog";
1191 let model = AutosarModel::new();
1192 let result = model.load_buffer(FILEBUF.as_bytes(), "test", true);
1194 assert!(result.is_ok());
1195 let result = model.load_buffer(FILEBUF2.as_bytes(), "other", true);
1197 assert!(result.is_ok());
1198 let result = model.load_buffer(FILEBUF.as_bytes(), "test", true);
1200 assert!(result.is_err());
1201 let result = model.load_buffer(FILEBUF3.as_bytes(), "test2", true);
1203 assert!(result.is_err());
1204 let result = model.load_buffer(NON_ARXML.as_bytes(), "nonsense", true);
1206 assert!(result.is_err());
1207 }
1208
1209 #[test]
1210 fn load_file() {
1211 let dir = tempdir().unwrap();
1212
1213 let model = AutosarModel::new();
1214 let filename = dir.path().with_file_name("nonexistent.arxml");
1215 assert!(model.load_file(&filename, true).is_err());
1216
1217 let filename = dir.path().with_file_name("test.arxml");
1218 model.create_file(&filename, AutosarVersion::LATEST).unwrap();
1219 model
1220 .root_element()
1221 .create_sub_element(ElementName::ArPackages)
1222 .and_then(|ap| ap.create_named_sub_element(ElementName::ArPackage, "Pkg"))
1223 .unwrap();
1224 model.write().unwrap();
1225
1226 assert!(filename.exists());
1227
1228 let model = AutosarModel::new();
1230 model.load_file(&filename, true).unwrap();
1231 let el_pkg = model.get_element_by_path("/Pkg");
1232 assert!(el_pkg.is_some());
1233 }
1234
1235 #[test]
1236 fn data_merge() {
1237 const FILEBUF1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1238 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1239 <AR-PACKAGES>
1240 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
1241 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
1242 <SHORT-NAME>BswModuleValues</SHORT-NAME>
1243 <PARAMETER-VALUES>
1244 <ECUC-NUMERICAL-PARAM-VALUE>
1245 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
1246 </ECUC-NUMERICAL-PARAM-VALUE>
1247 <ECUC-NUMERICAL-PARAM-VALUE>
1248 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
1249 </ECUC-NUMERICAL-PARAM-VALUE>
1250 <ECUC-NUMERICAL-PARAM-VALUE>
1251 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
1252 </ECUC-NUMERICAL-PARAM-VALUE>
1253 </PARAMETER-VALUES>
1254 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
1255 </ELEMENTS></AR-PACKAGE>
1256 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
1257 <AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
1258 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1259 const FILEBUF2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1260 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1261 <AR-PACKAGES>
1262 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
1263 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
1264 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
1265 <SHORT-NAME>BswModuleValues</SHORT-NAME>
1266 <PARAMETER-VALUES>
1267 <ECUC-NUMERICAL-PARAM-VALUE>
1268 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
1269 </ECUC-NUMERICAL-PARAM-VALUE>
1270 <ECUC-NUMERICAL-PARAM-VALUE>
1271 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
1272 </ECUC-NUMERICAL-PARAM-VALUE>
1273 </PARAMETER-VALUES>
1274 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
1275 </ELEMENTS></AR-PACKAGE>
1276 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1277 let model = AutosarModel::new();
1280 let (file1, _) = model.load_buffer(FILEBUF1, "test1", true).unwrap();
1281 let file1_elemcount = file1.elements_dfs().count();
1282 let (file2, _) = model.load_buffer(FILEBUF2, "test2", true).unwrap();
1283 let file2_elemcount = file2.elements_dfs().count();
1284 let model_elemcount = model.elements_dfs().count();
1285 assert_eq!(file1_elemcount, model_elemcount);
1286 assert!(file1_elemcount > file2_elemcount);
1287 let (local, fileset) = model.root_element().file_membership().unwrap();
1289 assert!(local);
1290 assert_eq!(fileset.len(), 2);
1291
1292 let el_pkg_c = model.get_element_by_path("/Pkg_C").unwrap();
1293 let (local, fileset) = el_pkg_c.file_membership().unwrap();
1294 assert!(local);
1295 assert_eq!(fileset.len(), 1);
1296 let el_npv2 = model
1297 .get_element_by_path("/Pkg_A/BswModule/BswModuleValues")
1298 .and_then(|bmv| bmv.get_sub_element(ElementName::ParameterValues))
1299 .and_then(|pv| pv.get_sub_element_at(2))
1300 .unwrap();
1301 let (loc, fm) = el_npv2.file_membership().unwrap();
1302 assert!(loc);
1303 assert_eq!(fm.len(), 1);
1304
1305 const ERRFILE1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1308 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1309 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1310 <ELEMENTS>
1311 <SYSTEM-TIMING>
1312 <SHORT-NAME>SystemTimings</SHORT-NAME>
1313 <CATEGORY>CAT</CATEGORY>
1314 <TIMING-RESOURCE>
1315 <SHORT-NAME>Name_One</SHORT-NAME>
1316 </TIMING-RESOURCE>
1317 </SYSTEM-TIMING>
1318 </ELEMENTS>
1319 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
1320 const ERRFILE2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1321 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1322 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1323 <ELEMENTS>
1324 <SYSTEM-TIMING>
1325 <SHORT-NAME>SystemTimings</SHORT-NAME>
1326 <TIMING-RESOURCE>
1327 <SHORT-NAME>Name_Two</SHORT-NAME>
1328 </TIMING-RESOURCE>
1329 </SYSTEM-TIMING>
1330 </ELEMENTS>
1331 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
1332 let model = AutosarModel::new();
1333 let result = model.load_buffer(ERRFILE1, "test1", true);
1334 assert!(result.is_ok());
1335 let result = model.load_buffer(ERRFILE2, "test2", true);
1336 let error = result.unwrap_err();
1337 assert!(matches!(error, AutosarDataError::InvalidFileMerge { .. }));
1338
1339 const ERRFILE3: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1343 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1344 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1345 <ELEMENTS>
1346 <COMPU-METHOD><SHORT-NAME>compu</SHORT-NAME>
1347 <COMPU-INTERNAL-TO-PHYS>
1348 <COMPU-SCALES>
1349 <COMPU-SCALE><COMPU-CONST></COMPU-CONST></COMPU-SCALE>
1350 </COMPU-SCALES>
1351 </COMPU-INTERNAL-TO-PHYS>
1352 </COMPU-METHOD>
1353 </ELEMENTS>
1354 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
1355 const ERRFILE4: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1356 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1357 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1358 <ELEMENTS>
1359 <COMPU-METHOD><SHORT-NAME>compu</SHORT-NAME>
1360 <COMPU-INTERNAL-TO-PHYS>
1361 <COMPU-SCALES>
1362 <COMPU-SCALE><COMPU-RATIONAL-COEFFS></COMPU-RATIONAL-COEFFS></COMPU-SCALE>
1363 </COMPU-SCALES>
1364 </COMPU-INTERNAL-TO-PHYS>
1365 </COMPU-METHOD>
1366 </ELEMENTS>
1367 </AR-PACKAGE></AR-PACKAGES></AUTOSAR>"#.as_bytes();
1368 let model = AutosarModel::new();
1369 let result = model.load_buffer(ERRFILE3, "test3", true);
1370 assert!(result.is_ok());
1371 let result = model.load_buffer(ERRFILE4, "test4", true);
1372 let error = result.unwrap_err();
1373 assert!(matches!(error, AutosarDataError::InvalidFileMerge { .. }));
1374
1375 const FILEBUF3: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1377 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1378 <AR-PACKAGES>
1379 <AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME></AR-PACKAGE>
1380 <AR-PACKAGE><SHORT-NAME>Package2</SHORT-NAME></AR-PACKAGE>
1381 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1382 const FILEBUF4: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1383 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1384 <AR-PACKAGES>
1385 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1386 let model_a = AutosarModel::new();
1387 model_a.load_buffer(FILEBUF3, "test5", true).unwrap();
1388 model_a.load_buffer(FILEBUF4, "test6", true).unwrap();
1389 let model_b = AutosarModel::new();
1391 model_b.load_buffer(FILEBUF4, "test5", true).unwrap();
1392 model_b.load_buffer(FILEBUF3, "test6", true).unwrap();
1393 model_a.sort();
1395 let model_a_txt = model_a.root_element().serialize();
1396 model_b.sort();
1397 let model_b_txt = model_b.root_element().serialize();
1398 assert_eq!(model_a_txt, model_b_txt);
1399 }
1400
1401 #[test]
1402 fn remove_file() {
1403 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1404 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1405 <AR-PACKAGES>
1406 <AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME></AR-PACKAGE>
1407 </AR-PACKAGES></AUTOSAR>"#;
1408 const FILEBUF2: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1409 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00049.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1410 <AR-PACKAGES>
1411 <AR-PACKAGE><SHORT-NAME>Package</SHORT-NAME>
1412 <ELEMENTS><CAN-CLUSTER><SHORT-NAME>CAN_Cluster</SHORT-NAME></CAN-CLUSTER></ELEMENTS>
1413 </AR-PACKAGE>
1414 </AR-PACKAGES></AUTOSAR>"#;
1415 const FILEBUF3: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1416 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1417 <AR-PACKAGES>
1418 <AR-PACKAGE><SHORT-NAME>Package2</SHORT-NAME>
1419 <ELEMENTS><SYSTEM><SHORT-NAME>System</SHORT-NAME>
1420 <FIBEX-ELEMENTS><FIBEX-ELEMENT-REF-CONDITIONAL>
1421 <FIBEX-ELEMENT-REF DEST="CAN-CLUSTER">/Package/CAN_Cluster</FIBEX-ELEMENT-REF>
1422 </FIBEX-ELEMENT-REF-CONDITIONAL></FIBEX-ELEMENTS>
1423 </SYSTEM></ELEMENTS></AR-PACKAGE>
1424 </AR-PACKAGES></AUTOSAR>"#;
1425 let model = AutosarModel::new();
1427 let (file, _) = model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
1428 assert_eq!(model.files().count(), 1);
1429 assert_eq!(model.identifiable_elements().count(), 1);
1430 model.remove_file(&file);
1431 assert_eq!(model.files().count(), 0);
1432 assert_eq!(model.identifiable_elements().count(), 0);
1433 let model = AutosarModel::new();
1435 model.load_buffer(FILEBUF.as_bytes(), "test1", true).unwrap();
1436 assert_eq!(model.files().count(), 1);
1437 let modeltxt_1 = model.root_element().serialize();
1438 let (file2, _) = model.load_buffer(FILEBUF2.as_bytes(), "test2", true).unwrap();
1439 assert_eq!(model.files().count(), 2);
1440 let modeltxt_1_2 = model.root_element().serialize();
1441 assert_ne!(modeltxt_1, modeltxt_1_2);
1442 let (file3, _) = model.load_buffer(FILEBUF3.as_bytes(), "test3", true).unwrap();
1443 assert_eq!(model.files().count(), 3);
1444 let modeltxt_1_2_3 = model.root_element().serialize();
1445 assert_ne!(modeltxt_1_2, modeltxt_1_2_3);
1446 model.get_element_by_path("/Package2/System").unwrap();
1447 model.remove_file(&file3);
1448 let modeltxt_1_2_x = model.root_element().serialize();
1450 assert_eq!(modeltxt_1_2, modeltxt_1_2_x);
1451 model.remove_file(&file2);
1452 let modeltxt_1_x_x = model.root_element().serialize();
1454 assert_eq!(modeltxt_1, modeltxt_1_x_x);
1455 assert_eq!(model.files().count(), 1);
1456 }
1457
1458 #[test]
1459 fn refcount() {
1460 let model = AutosarModel::default();
1461 let weak = model.downgrade();
1462 let project2 = weak.upgrade();
1463 assert_eq!(Arc::strong_count(&model.0), 2);
1464 assert_eq!(model, project2.unwrap());
1465 }
1466
1467 #[test]
1468 fn identifiables_iterator() {
1469 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1470 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1471 <AR-PACKAGES>
1472 <AR-PACKAGE><SHORT-NAME>OuterPackage1</SHORT-NAME>
1473 <AR-PACKAGES>
1474 <AR-PACKAGE><SHORT-NAME>InnerPackage1</SHORT-NAME></AR-PACKAGE>
1475 <AR-PACKAGE><SHORT-NAME>InnerPackage2</SHORT-NAME></AR-PACKAGE>
1476 </AR-PACKAGES>
1477 </AR-PACKAGE>
1478 <AR-PACKAGE><SHORT-NAME>OuterPackage2</SHORT-NAME>
1479 <AR-PACKAGES>
1480 <AR-PACKAGE><SHORT-NAME>InnerPackage1</SHORT-NAME></AR-PACKAGE>
1481 <AR-PACKAGE><SHORT-NAME>InnerPackage2</SHORT-NAME></AR-PACKAGE>
1482 </AR-PACKAGES>
1483 </AR-PACKAGE>
1484 </AR-PACKAGES></AUTOSAR>"#;
1485 let model = AutosarModel::new();
1486 model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
1487 let mut identifiable_elements = model.identifiable_elements().collect::<Vec<_>>();
1488 identifiable_elements.sort_by(|a, b| a.0.cmp(&b.0));
1489 assert_eq!(identifiable_elements[0].0, "/OuterPackage1");
1490 assert_eq!(identifiable_elements[1].0, "/OuterPackage1/InnerPackage1");
1491 assert_eq!(identifiable_elements[2].0, "/OuterPackage1/InnerPackage2");
1492 assert_eq!(identifiable_elements[3].0, "/OuterPackage2");
1493 assert_eq!(identifiable_elements[4].0, "/OuterPackage2/InnerPackage1");
1494 assert_eq!(identifiable_elements[5].0, "/OuterPackage2/InnerPackage2");
1495 }
1496
1497 #[test]
1498 fn check_references() {
1499 const FILEBUF: &str = r#"<?xml version="1.0" encoding="utf-8"?>
1500 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1501 <AR-PACKAGES><AR-PACKAGE><SHORT-NAME>Pkg</SHORT-NAME>
1502 <ELEMENTS>
1503 <SYSTEM><SHORT-NAME>System</SHORT-NAME>
1504 <FIBEX-ELEMENTS>
1505 <FIBEX-ELEMENT-REF-CONDITIONAL>
1506 <FIBEX-ELEMENT-REF DEST="ECU-INSTANCE">/Pkg/EcuInstance</FIBEX-ELEMENT-REF>
1507 </FIBEX-ELEMENT-REF-CONDITIONAL>
1508 <FIBEX-ELEMENT-REF-CONDITIONAL>
1509 <FIBEX-ELEMENT-REF DEST="I-SIGNAL-I-PDU">/Some/Invalid/Path</FIBEX-ELEMENT-REF>
1510 </FIBEX-ELEMENT-REF-CONDITIONAL>
1511 <FIBEX-ELEMENT-REF-CONDITIONAL>
1512 <FIBEX-ELEMENT-REF DEST="I-SIGNAL">/Pkg/System</FIBEX-ELEMENT-REF>
1513 </FIBEX-ELEMENT-REF-CONDITIONAL>
1514 </FIBEX-ELEMENTS>
1515 </SYSTEM>
1516 <ECU-INSTANCE><SHORT-NAME>EcuInstance</SHORT-NAME></ECU-INSTANCE>
1517 </ELEMENTS>
1518 </AR-PACKAGE>
1519 </AR-PACKAGES></AUTOSAR>"#;
1520 let model = AutosarModel::new();
1521 model.load_buffer(FILEBUF.as_bytes(), "test", true).unwrap();
1522 let el_system = model.get_element_by_path("/Pkg/System").unwrap();
1523 let el_fibex_elements = el_system.get_sub_element(ElementName::FibexElements).unwrap();
1524 let el_fibex_element_ref = el_fibex_elements
1525 .create_sub_element(ElementName::FibexElementRefConditional)
1526 .and_then(|ferc| ferc.create_sub_element(ElementName::FibexElementRef))
1527 .unwrap();
1528 el_fibex_element_ref.set_character_data("/Pkg/System").unwrap();
1529 assert_eq!(model.0.read().reference_origins.len(), 3);
1535
1536 let el_fbx_ref1 = el_fibex_elements
1538 .get_sub_element_at(0)
1539 .and_then(|ferc| ferc.get_sub_element(ElementName::FibexElementRef))
1540 .unwrap();
1541 assert_eq!(
1542 el_fbx_ref1.get_reference_target().unwrap().element_name(),
1543 ElementName::EcuInstance
1544 );
1545
1546 let invalid_refs = model
1547 .check_references()
1548 .iter()
1549 .filter_map(WeakElement::upgrade)
1550 .collect::<Vec<_>>();
1551 assert_eq!(invalid_refs.len(), 3);
1552 let ref0 = &invalid_refs[0];
1553 assert_eq!(ref0.element_name(), ElementName::FibexElementRef);
1554 let refpath = ref0.character_data().and_then(|cdata| cdata.string_value()).unwrap();
1555 assert!(refpath == "/Pkg/System" || refpath == "/Some/Invalid/Path");
1557
1558 model.get_element_by_path("/Pkg/EcuInstance").unwrap();
1559 let refs = model.get_references_to("/Pkg/EcuInstance");
1560 assert_eq!(refs.len(), 1);
1561 let refs = model.get_references_to("nonexistent");
1562 assert!(refs.is_empty());
1563 }
1564
1565 #[test]
1566 fn serialize_files() {
1567 let model = AutosarModel::default();
1568 let file1 = model.create_file("filename1", AutosarVersion::Autosar_00042).unwrap();
1569 let file2 = model.create_file("filename2", AutosarVersion::Autosar_00042).unwrap();
1570
1571 let result = model.serialize_files();
1572 assert_eq!(result.len(), 2);
1573 assert_eq!(
1574 result.get(&PathBuf::from("filename1")).unwrap(),
1575 &file1.serialize().unwrap()
1576 );
1577 assert_eq!(
1578 result.get(&PathBuf::from("filename2")).unwrap(),
1579 &file2.serialize().unwrap()
1580 );
1581 }
1582
1583 #[test]
1584 fn duplicate() {
1585 let model = AutosarModel::new();
1586 let file1 = model.create_file("filename1", AutosarVersion::Autosar_00042).unwrap();
1587 let file2 = model.create_file("filename2", AutosarVersion::Autosar_00042).unwrap();
1588 let el_ar_packages = model
1589 .root_element()
1590 .create_sub_element(ElementName::ArPackages)
1591 .unwrap();
1592 let el_pkg1 = el_ar_packages
1593 .create_named_sub_element(ElementName::ArPackage, "pkg1")
1594 .unwrap();
1595 let el_pkg2 = el_ar_packages
1596 .create_named_sub_element(ElementName::ArPackage, "pkg2")
1597 .unwrap();
1598
1599 assert_eq!(el_ar_packages.file_membership().unwrap().1.len(), 2);
1600 el_pkg1.remove_from_file(&file2).unwrap();
1601 assert_eq!(el_pkg1.file_membership().unwrap().1.len(), 1);
1602 el_pkg2.remove_from_file(&file1).unwrap();
1603 assert_eq!(el_pkg2.file_membership().unwrap().1.len(), 1);
1604
1605 let model2 = model.duplicate().unwrap();
1606 assert_eq!(model2.files().count(), 2);
1607 let mut files_iter = model2.files();
1608 let mut model2_file1 = files_iter.next().unwrap();
1610 let mut model2_file2 = files_iter.next().unwrap();
1611 if model2_file1.filename() != file1.filename() {
1613 std::mem::swap(&mut model2_file1, &mut model2_file2);
1614 }
1615
1616 assert_eq!(file1.filename(), model2_file1.filename());
1617 assert_eq!(file2.filename(), model2_file2.filename());
1618 assert_eq!(file1.serialize().unwrap(), model2_file1.serialize().unwrap());
1619 assert_eq!(file2.serialize().unwrap(), model2_file2.serialize().unwrap());
1620 }
1621
1622 #[test]
1623 fn write() {
1624 let model = AutosarModel::default();
1625 model.write().unwrap();
1627
1628 let dir = tempdir().unwrap();
1629 let filename = dir.path().with_file_name("new.arxml");
1630 model.create_file(&filename, AutosarVersion::LATEST).unwrap();
1631 model.write().unwrap();
1632 assert!(filename.exists());
1633
1634 let filename = PathBuf::from("nonexistent/dir/some_file.arxml");
1635 let model = AutosarModel::default();
1636 model.create_file(&filename, AutosarVersion::LATEST).unwrap();
1638 let result = model.write();
1640 assert!(result.is_err());
1641 }
1642
1643 #[test]
1644 fn traits() {
1645 let model = AutosarModel::new();
1647 let model_cloned = model.clone();
1648 assert_eq!(model, model_cloned);
1649 assert_eq!(format!("{model:#?}"), format!("{model_cloned:#?}"));
1650 let mut hashset = HashSet::<AutosarModel>::new();
1651 hashset.insert(model);
1652 let inserted = hashset.insert(model_cloned);
1653 assert!(!inserted);
1654
1655 let cdata = CharacterData::String("x".to_string());
1657 let cdata2 = cdata.clone();
1658 assert_eq!(cdata, cdata2);
1659 assert_eq!(format!("{cdata:#?}"), format!("{cdata2:#?}"));
1660
1661 let ct: ContentType = ContentType::Elements;
1663 let ct2 = ct;
1664 assert_eq!(ct, ct2);
1665 assert_eq!(format!("{ct:#?}"), format!("{ct2:#?}"));
1666 }
1667
1668 #[test]
1669 fn elements_dfs_with_max_depth() {
1670 const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
1671 <AUTOSAR xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00050.xsd" xmlns="http://autosar.org/schema/r4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
1672 <AR-PACKAGES>
1673 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
1674 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
1675 <SHORT-NAME>BswModuleValues</SHORT-NAME>
1676 <PARAMETER-VALUES>
1677 <ECUC-NUMERICAL-PARAM-VALUE>
1678 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
1679 </ECUC-NUMERICAL-PARAM-VALUE>
1680 <ECUC-NUMERICAL-PARAM-VALUE>
1681 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
1682 </ECUC-NUMERICAL-PARAM-VALUE>
1683 <ECUC-NUMERICAL-PARAM-VALUE>
1684 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
1685 </ECUC-NUMERICAL-PARAM-VALUE>
1686 </PARAMETER-VALUES>
1687 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
1688 </ELEMENTS></AR-PACKAGE>
1689 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
1690 <AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
1691 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
1692 let model = AutosarModel::new();
1693 let (_, _) = model.load_buffer(FILEBUF, "test1", true).unwrap();
1694 let all_count = model.elements_dfs().count();
1695 let lvl2_count = model.elements_dfs_with_max_depth(2).count();
1696 assert!(all_count > lvl2_count);
1697 for elem in model.elements_dfs_with_max_depth(2) {
1698 assert!(elem.0 <= 2);
1699 }
1700 }
1701
1702 #[test]
1703 fn model_merge() {
1704 const FILE_A: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
1706<AUTOSAR xmlns="http://autosar.org/schema/r4.0"
1707 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1708 xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd">
1709 <AR-PACKAGES>
1710 <AR-PACKAGE>
1711 <SHORT-NAME>EcucModuleConfigurationValuess</SHORT-NAME>
1712 <ELEMENTS>
1713 <ECUC-MODULE-CONFIGURATION-VALUES>
1714 <SHORT-NAME>A</SHORT-NAME>
1715 <DEFINITION-REF DEST="ECUC-MODULE-DEF">/AUTOSAR_A</DEFINITION-REF>
1716 <CONTAINERS>
1717 <ECUC-CONTAINER-VALUE>
1718 <SHORT-NAME>AB</SHORT-NAME>
1719 <DEFINITION-REF DEST="ECUC-PARAM-CONF-CONTAINER-DEF">/AUTOSAR_A/B</DEFINITION-REF>
1720 <PARAMETER-VALUES>
1721 <ECUC-NUMERICAL-PARAM-VALUE>
1722 <DEFINITION-REF DEST="ECUC-FLOAT-PARAM-DEF">/AUTOSAR_A/B/D</DEFINITION-REF>
1723 <VALUE>0.01</VALUE>
1724 </ECUC-NUMERICAL-PARAM-VALUE>
1725 <ECUC-TEXTUAL-PARAM-VALUE>
1726 <DEFINITION-REF DEST="ECUC-ENUMERATION-PARAM-DEF">/AUTOSAR_A/B/E</DEFINITION-REF>
1727 <VALUE>ABC42</VALUE>
1728 </ECUC-TEXTUAL-PARAM-VALUE>
1729 </PARAMETER-VALUES>
1730 </ECUC-CONTAINER-VALUE>
1731 </CONTAINERS>
1732 </ECUC-MODULE-CONFIGURATION-VALUES>
1733 </ELEMENTS>
1734 </AR-PACKAGE>
1735 </AR-PACKAGES>
1736</AUTOSAR>
1737 "#;
1738
1739 const FILE_B: &[u8] = br#"<?xml version="1.0" encoding="utf-8"?>
1740<AUTOSAR xmlns="http://autosar.org/schema/r4.0"
1741 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1742 xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_00048.xsd">
1743 <AR-PACKAGES>
1744 <AR-PACKAGE>
1745 <SHORT-NAME>EcucModuleConfigurationValuess</SHORT-NAME>
1746 <ELEMENTS>
1747 <ECUC-MODULE-CONFIGURATION-VALUES>
1748 <SHORT-NAME>A</SHORT-NAME>
1749 <DEFINITION-REF DEST="ECUC-MODULE-DEF">/AUTOSAR_A</DEFINITION-REF>
1750 <CONTAINERS>
1751 <ECUC-CONTAINER-VALUE>
1752 <SHORT-NAME>AB</SHORT-NAME>
1753 <DEFINITION-REF DEST="ECUC-PARAM-CONF-CONTAINER-DEF">/AUTOSAR_A/B</DEFINITION-REF>
1754 <PARAMETER-VALUES>
1755 <ECUC-NUMERICAL-PARAM-VALUE>
1756 <DEFINITION-REF DEST="ECUC-INTEGER-PARAM-DEF">/AUTOSAR_A/B/C</DEFINITION-REF>
1757 <VALUE>0</VALUE>
1758 </ECUC-NUMERICAL-PARAM-VALUE>
1759 <ECUC-NUMERICAL-PARAM-VALUE>
1760 <DEFINITION-REF DEST="ECUC-FLOAT-PARAM-DEF">/AUTOSAR_A/B/D</DEFINITION-REF>
1761 <VALUE>0.01</VALUE>
1762 </ECUC-NUMERICAL-PARAM-VALUE>
1763 </PARAMETER-VALUES>
1764 </ECUC-CONTAINER-VALUE>
1765 </CONTAINERS>
1766 </ECUC-MODULE-CONFIGURATION-VALUES>
1767 </ELEMENTS>
1768 </AR-PACKAGE>
1769 </AR-PACKAGES>
1770</AUTOSAR>"#;
1771
1772 let model = AutosarModel::new();
1774 let (_, _) = model.load_buffer(FILE_A, "file_a", true).unwrap();
1775 let (_, _) = model.load_buffer(FILE_B, "file_b", true).unwrap();
1776 model.sort();
1778 let model_txt = model.root_element().serialize();
1779
1780 let model2 = AutosarModel::new();
1781 let (_, _) = model2.load_buffer(FILE_B, "file_b", true).unwrap();
1782 let (_, _) = model2.load_buffer(FILE_A, "file_a", true).unwrap();
1783 model2.sort();
1785 let model2_txt = model2.root_element().serialize();
1786
1787 println!("{}\n\n=======================\n{}\n\n", model_txt, model2_txt);
1788 assert_eq!(model_txt, model2_txt);
1789 }
1790
1791 #[test]
1792 fn model_merge_2() {
1793 const FILEBUF1: &[u8] = br#"<?xml version="1.0" encoding="UTF-8"?>
1795<AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-3-0.xsd">
1796 <AR-PACKAGES>
1797 <AR-PACKAGE>
1798 <SHORT-NAME>BSWMD_Package</SHORT-NAME>
1799 <ELEMENTS>
1800 <BSW-MODULE-DESCRIPTION>
1801 <SHORT-NAME>BSWMD</SHORT-NAME>
1802 <IMPLEMENTED-ENTRYS>
1803 <BSW-MODULE-ENTRY-REF-CONDITIONAL>
1804 <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_A0</BSW-MODULE-ENTRY-REF>
1805 </BSW-MODULE-ENTRY-REF-CONDITIONAL>
1806 </IMPLEMENTED-ENTRYS>
1807 </BSW-MODULE-DESCRIPTION>
1808 </ELEMENTS>
1809 </AR-PACKAGE>
1810 </AR-PACKAGES>
1811</AUTOSAR>"#;
1812 const FILEBUF2: &[u8] = br#"<?xml version="1.0" encoding="UTF-8"?>
1813<AUTOSAR xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://autosar.org/schema/r4.0" xsi:schemaLocation="http://autosar.org/schema/r4.0 AUTOSAR_4-3-0.xsd">
1814 <AR-PACKAGES>
1815 <AR-PACKAGE>
1816 <SHORT-NAME>BSWMD_Package</SHORT-NAME>
1817 <ELEMENTS>
1818 <BSW-MODULE-DESCRIPTION>
1819 <SHORT-NAME>BSWMD</SHORT-NAME>
1820 <IMPLEMENTED-ENTRYS>
1821 <BSW-MODULE-ENTRY-REF-CONDITIONAL>
1822 <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_B0</BSW-MODULE-ENTRY-REF>
1823 </BSW-MODULE-ENTRY-REF-CONDITIONAL>
1824 <BSW-MODULE-ENTRY-REF-CONDITIONAL>
1825 <BSW-MODULE-ENTRY-REF DEST="BSW-MODULE-ENTRY">/path/to/entry_B1</BSW-MODULE-ENTRY-REF>
1826 </BSW-MODULE-ENTRY-REF-CONDITIONAL>
1827 </IMPLEMENTED-ENTRYS>
1828 </BSW-MODULE-DESCRIPTION>
1829 </ELEMENTS>
1830 </AR-PACKAGE>
1831 </AR-PACKAGES>
1832</AUTOSAR>"#;
1833
1834 let model = AutosarModel::new();
1835 let (_, _) = model.load_buffer(FILEBUF1, "file1", true).unwrap();
1836 let (_, _) = model.load_buffer(FILEBUF2, "file2", true).unwrap();
1837
1838 let a0_refs = model.get_references_to("/path/to/entry_A0");
1839 assert!(a0_refs.len() == 1);
1840 assert!(a0_refs[0].upgrade().is_some());
1841 let b0_refs = model.get_references_to("/path/to/entry_B0");
1842 assert!(b0_refs.len() == 1);
1843 assert!(b0_refs[0].upgrade().is_some());
1844 let b1_refs = model.get_references_to("/path/to/entry_B1");
1845 assert!(b1_refs.len() == 1);
1846 assert!(b1_refs[0].upgrade().is_some());
1847 }
1848}