1use std::hash::Hash;
2
3use crate::*;
4
5impl ArxmlFile {
6 pub(crate) fn new<P: AsRef<Path>>(filename: P, version: AutosarVersion, model: &AutosarModel) -> Self {
7 ArxmlFileRaw {
8 version,
9 model: model.downgrade(),
10 filename: filename.as_ref().to_path_buf(),
11 xml_standalone: None,
12 }
13 .wrap()
14 }
15
16 #[must_use]
27 pub fn filename(&self) -> PathBuf {
28 self.0.read().filename.clone()
29 }
30
31 #[must_use]
42 pub fn version(&self) -> AutosarVersion {
43 self.0.read().version
44 }
45
46 pub fn set_version(&self, new_ver: AutosarVersion) -> Result<(), AutosarDataError> {
67 let (compat_errors, _) = self.check_version_compatibility(new_ver);
68 if compat_errors.is_empty() {
69 let mut file = self.0.write();
70 file.version = new_ver;
71 Ok(())
72 } else {
73 Err(AutosarDataError::VersionIncompatibleData { version: new_ver })
74 }
75 }
76
77 #[must_use]
91 pub fn check_version_compatibility(&self, target_version: AutosarVersion) -> (Vec<CompatibilityError>, u32) {
92 if let Ok(model) = self.model() {
93 model
94 .root_element()
95 .check_version_compatibility(&self.downgrade(), target_version)
96 } else {
97 (Vec::new(), 0)
98 }
99 }
100
101 pub fn set_filename<P: AsRef<Path>>(&self, new_filename: P) -> Result<(), AutosarDataError> {
117 let new_filename = new_filename.as_ref().to_path_buf();
118 if self
119 .model()?
120 .files()
121 .map(|f| (f.clone(), f.filename()))
122 .any(|(file, filename)| file != *self && filename == new_filename)
123 {
124 Err(AutosarDataError::DuplicateFilenameError {
125 verb: "set_filename",
126 filename: new_filename,
127 })
128 } else {
129 self.0.write().filename = new_filename;
130 Ok(())
131 }
132 }
133
134 pub fn model(&self) -> Result<AutosarModel, AutosarDataError> {
154 let locked_file = self.0.write();
155 locked_file.model.upgrade().ok_or(AutosarDataError::ItemDeleted)
157 }
158
159 #[must_use]
174 pub fn elements_dfs(&self) -> ArxmlFileElementsDfsIterator {
175 ArxmlFileElementsDfsIterator::new(self, 0)
176 }
177
178 #[must_use]
198 pub fn elements_dfs_with_max_depth(&self, max_depth: usize) -> ArxmlFileElementsDfsIterator {
199 ArxmlFileElementsDfsIterator::new(self, max_depth)
200 }
201
202 pub fn serialize(&self) -> Result<String, AutosarDataError> {
218 let model = self.model()?;
219 if !model.root_element().file_membership()?.1.contains(&self.downgrade()) {
220 return Err(AutosarDataError::EmptyFile);
221 }
222
223 let mut outstring = String::with_capacity(1024 * 1024);
224
225 match self.xml_standalone() {
226 Some(true) => outstring.push_str("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>"),
227 Some(false) => outstring.push_str("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>"),
228 None => outstring.push_str("<?xml version=\"1.0\" encoding=\"utf-8\"?>"),
229 }
230 model.0.write().set_version(self.0.read().version);
231 model
232 .root_element()
233 .serialize_internal(&mut outstring, 0, false, &Some(self.downgrade()));
234
235 Ok(outstring)
236 }
237
238 #[must_use]
257 pub fn xml_standalone(&self) -> Option<bool> {
258 self.0.read().xml_standalone
259 }
260
261 #[must_use]
277 pub fn downgrade(&self) -> WeakArxmlFile {
278 WeakArxmlFile(Arc::downgrade(&self.0))
279 }
280}
281
282impl ArxmlFileRaw {
283 pub(crate) fn wrap(self) -> ArxmlFile {
284 ArxmlFile(Arc::new(RwLock::new(self)))
285 }
286}
287
288impl PartialEq for ArxmlFile {
289 fn eq(&self, other: &Self) -> bool {
290 Arc::as_ptr(&self.0) == Arc::as_ptr(&other.0)
291 }
292}
293
294impl Eq for ArxmlFile {}
295
296impl Hash for ArxmlFile {
297 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
298 state.write_usize(Arc::as_ptr(&self.0) as usize);
299 }
300}
301
302impl std::fmt::Debug for ArxmlFile {
303 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304 let self_locked = self.0.read();
305 f.debug_struct("ArxmlFile")
306 .field("filename", &self_locked.filename)
307 .field("version", &self_locked.version)
308 .field("model", &self_locked.model)
309 .field("xml_standalone", &self_locked.xml_standalone)
310 .finish()
311 }
312}
313
314impl WeakArxmlFile {
315 pub fn upgrade(&self) -> Option<ArxmlFile> {
319 Weak::upgrade(&self.0).map(ArxmlFile)
320 }
321}
322
323impl PartialEq for WeakArxmlFile {
324 fn eq(&self, other: &Self) -> bool {
325 Weak::as_ptr(&self.0) == Weak::as_ptr(&other.0)
326 }
327}
328
329impl Eq for WeakArxmlFile {}
330
331impl Hash for WeakArxmlFile {
332 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
333 state.write_usize(Weak::as_ptr(&self.0) as usize);
334 }
335}
336
337impl std::fmt::Debug for WeakArxmlFile {
338 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
339 if let Some(arxmlfile) = self.upgrade() {
340 f.write_fmt(format_args!("ArxmlFile:WeakRef ({})", arxmlfile.filename().display()))
341 } else {
342 f.write_fmt(format_args!("ArxmlFile:WeakRef {:p} (invalid)", Weak::as_ptr(&self.0)))
343 }
344 }
345}
346
347#[cfg(test)]
348mod test {
349 use super::*;
350
351 #[test]
352 fn create() {
353 let model = AutosarModel::new();
354 let result = model.create_file("test", AutosarVersion::Autosar_4_0_1);
355 assert!(result.is_ok());
356 }
357
358 #[test]
359 fn filename() {
360 let model = AutosarModel::new();
361 let result = model.create_file("test", AutosarVersion::Autosar_4_0_1);
362 let file = result.unwrap();
363 let filename = PathBuf::from("newname.arxml");
364 file.set_filename(filename.clone()).unwrap();
365 assert_eq!(file.filename(), filename);
366 }
367
368 #[test]
369 fn version() {
370 let model: AutosarModel = AutosarModel::new();
371 let file = model.create_file("test", AutosarVersion::Autosar_00051).unwrap();
372
373 let el_elements = model
374 .root_element()
375 .create_sub_element(ElementName::ArPackages)
376 .and_then(|arpkgs| arpkgs.create_named_sub_element(ElementName::ArPackage, "Pkg"))
377 .and_then(|arpkg| arpkg.create_sub_element(ElementName::Elements))
378 .unwrap();
379 let incompatible_elem = el_elements
380 .create_named_sub_element(ElementName::AdaptiveApplicationSwComponentType, "incompatible")
381 .unwrap();
382
383 let result = file.set_version(AutosarVersion::Autosar_4_0_1);
384 assert!(result.is_err());
385
386 el_elements.remove_sub_element(incompatible_elem).unwrap();
387
388 file.set_version(AutosarVersion::Autosar_4_0_1).unwrap();
389 assert_eq!(file.version(), AutosarVersion::Autosar_4_0_1);
390 }
391
392 #[test]
393 fn references() {
394 let model = AutosarModel::new();
395 let result = model.create_file("test", AutosarVersion::Autosar_4_0_1);
396 let file = result.unwrap();
397 let weak_file = file.downgrade();
398 let file2 = weak_file.upgrade().unwrap();
399 assert_eq!(Arc::strong_count(&file.0), 3); assert_eq!(file, file2);
401 }
402
403 #[test]
404 fn standalone() {
405 let model = AutosarModel::new();
406 let file_text = r#"<?xml version="1.0" encoding="utf-8" standalone="no"?>
407 <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">
408 </AUTOSAR>"#.as_bytes();
409 let (file, _warnings) = model.load_buffer(file_text, "filename.arxml", true).unwrap();
410 assert_eq!(file.xml_standalone(), Some(false));
411 }
412
413 #[test]
414 fn serialize() {
415 let model = AutosarModel::new();
416 let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
417 assert_eq!(file.model().unwrap(), model);
418 assert_eq!(model.root_element().element_name(), ElementName::Autosar);
419 let text = file.serialize().unwrap();
420 assert_eq!(
421 text,
422 r#"<?xml version="1.0" encoding="utf-8"?>
423<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"/>"#
424 );
425 file.0.write().xml_standalone = Some(false);
426 let text = file.serialize().unwrap();
427 assert_eq!(
428 text,
429 r#"<?xml version="1.0" encoding="utf-8" standalone="no"?>
430<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"/>"#
431 );
432 file.0.write().xml_standalone = Some(true);
433 let text = file.serialize().unwrap();
434 assert_eq!(
435 text,
436 r#"<?xml version="1.0" encoding="utf-8" standalone="yes"?>
437<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"/>"#
438 );
439 }
440
441 #[test]
442 fn elements_dfs_iterator() {
443 const FILEBUF_1: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
444 <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">
445 <AR-PACKAGES>
446 <AR-PACKAGE>
447 <SHORT-NAME>Pkg</SHORT-NAME>
448 <ELEMENTS>
449 <SYSTEM><SHORT-NAME>System</SHORT-NAME></SYSTEM>
450 </ELEMENTS>
451 </AR-PACKAGE>
452 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
453 const FILEBUF_2: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
454 <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">
455 <AR-PACKAGES>
456 <AR-PACKAGE>
457 <SHORT-NAME>Pkg2</SHORT-NAME>
458 <ELEMENTS>
459 <APPLICATION-PRIMITIVE-DATA-TYPE><SHORT-NAME>DataType</SHORT-NAME></APPLICATION-PRIMITIVE-DATA-TYPE>
460 </ELEMENTS>
461 </AR-PACKAGE>
462 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
463
464 let model = AutosarModel::new();
465 let (file, _) = model.load_buffer(FILEBUF_1, "file1.arxml", false).unwrap();
466 let proj_elem_count = model.elements_dfs().count();
467 let file_elem_count = file.elements_dfs().count();
468 assert_eq!(proj_elem_count, file_elem_count);
469 model.load_buffer(FILEBUF_2, "file2.arxml", false).unwrap();
470 let proj_elem_count_2 = model.elements_dfs().count();
471 let file_elem_count_2 = file.elements_dfs().count();
472 assert!(proj_elem_count < proj_elem_count_2);
473 assert_eq!(file_elem_count, file_elem_count_2);
474 }
475
476 #[test]
477 fn elements_dfs_with_max_depth() {
478 const FILEBUF: &[u8] = r#"<?xml version="1.0" encoding="utf-8"?>
479 <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">
480 <AR-PACKAGES>
481 <AR-PACKAGE><SHORT-NAME>Pkg_A</SHORT-NAME><ELEMENTS>
482 <ECUC-MODULE-CONFIGURATION-VALUES><SHORT-NAME>BswModule</SHORT-NAME><CONTAINERS><ECUC-CONTAINER-VALUE>
483 <SHORT-NAME>BswModuleValues</SHORT-NAME>
484 <PARAMETER-VALUES>
485 <ECUC-NUMERICAL-PARAM-VALUE>
486 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_A</DEFINITION-REF>
487 </ECUC-NUMERICAL-PARAM-VALUE>
488 <ECUC-NUMERICAL-PARAM-VALUE>
489 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_B</DEFINITION-REF>
490 </ECUC-NUMERICAL-PARAM-VALUE>
491 <ECUC-NUMERICAL-PARAM-VALUE>
492 <DEFINITION-REF DEST="ECUC-BOOLEAN-PARAM-DEF">/REF_C</DEFINITION-REF>
493 </ECUC-NUMERICAL-PARAM-VALUE>
494 </PARAMETER-VALUES>
495 </ECUC-CONTAINER-VALUE></CONTAINERS></ECUC-MODULE-CONFIGURATION-VALUES>
496 </ELEMENTS></AR-PACKAGE>
497 <AR-PACKAGE><SHORT-NAME>Pkg_B</SHORT-NAME></AR-PACKAGE>
498 <AR-PACKAGE><SHORT-NAME>Pkg_C</SHORT-NAME></AR-PACKAGE>
499 </AR-PACKAGES></AUTOSAR>"#.as_bytes();
500 let model = AutosarModel::new();
501 let (file, _) = model.load_buffer(FILEBUF, "test1", true).unwrap();
502 let all_count = file.elements_dfs().count();
503 let lvl2_count = file.elements_dfs_with_max_depth(2).count();
504 assert!(all_count > lvl2_count);
505 for elem in file.elements_dfs_with_max_depth(2) {
506 assert!(elem.0 <= 2);
507 }
508 }
509
510 #[test]
511 fn multiple_files_1() {
512 let model = AutosarModel::new();
514 let file_a = model.create_file("a", AutosarVersion::LATEST).unwrap();
515 let file_b = model.create_file("b", AutosarVersion::LATEST).unwrap();
516 let el_a_packages = model
517 .root_element()
518 .create_sub_element(ElementName::ArPackages)
519 .unwrap();
520 let (_, fs) = el_a_packages.file_membership().unwrap();
523 assert!(fs.contains(&file_a.downgrade()));
524 assert!(fs.contains(&file_b.downgrade()));
525 }
526
527 #[test]
528 fn multiple_files_2() {
529 let model = AutosarModel::new();
532 let file_a = model.create_file("a", AutosarVersion::LATEST).unwrap();
533 let el_a_packages = model
534 .root_element()
535 .create_sub_element(ElementName::ArPackages)
536 .unwrap();
537 let file_b = model.create_file("b", AutosarVersion::LATEST).unwrap();
538 let (_, fs) = el_a_packages.file_membership().unwrap();
540 assert!(fs.contains(&file_a.downgrade()));
541 assert!(!fs.contains(&file_b.downgrade()));
542 }
543
544 #[test]
545 fn multiple_files_3() {
546 let model = AutosarModel::new();
548 let file_a = model.create_file("a", AutosarVersion::LATEST).unwrap();
549 let el_ar_packages = model
550 .root_element()
551 .create_sub_element(ElementName::ArPackages)
552 .unwrap();
553 let el_pkg1 = el_ar_packages
554 .create_named_sub_element(ElementName::ArPackage, "Pkg1")
555 .unwrap();
556 let el_pkg2 = el_ar_packages
557 .create_named_sub_element(ElementName::ArPackage, "Pkg2")
558 .unwrap();
559 let file_b = model.create_file("b", AutosarVersion::LATEST).unwrap();
560 let (_, fs) = el_pkg1.file_membership().unwrap();
561 assert!(fs.contains(&file_a.downgrade())); assert!(!fs.contains(&file_b.downgrade())); let (_, fs) = el_pkg2.file_membership().unwrap();
564 assert!(fs.contains(&file_a.downgrade())); assert!(!fs.contains(&file_b.downgrade())); el_pkg2.add_to_file(&file_b).unwrap();
569 let (_, fs) = el_pkg1.file_membership().unwrap();
570 assert!(fs.contains(&file_a.downgrade())); assert!(!fs.contains(&file_b.downgrade())); let (_, fs) = el_pkg2.file_membership().unwrap();
573 assert!(fs.contains(&file_a.downgrade())); assert!(fs.contains(&file_b.downgrade())); let (_, fs) = el_ar_packages.file_membership().unwrap();
578 assert!(fs.contains(&file_a.downgrade())); assert!(fs.contains(&file_b.downgrade())); let (_, fs) = el_pkg2.file_membership().unwrap();
583 assert!(fs.contains(&file_a.downgrade())); assert!(fs.contains(&file_b.downgrade())); el_pkg2.remove_from_file(&file_a).unwrap();
586 let (_, fs) = el_pkg2.file_membership().unwrap();
587 assert!(!fs.contains(&file_a.downgrade())); assert!(fs.contains(&file_b.downgrade())); let el_elements = el_pkg1.create_sub_element(ElementName::Elements).unwrap();
592 let result = el_elements.add_to_file(&file_a);
593 assert!(matches!(result, Err(AutosarDataError::FilesetModificationForbidden)));
594 let result: Result<(), AutosarDataError> = el_elements.remove_from_file(&file_a);
595 assert!(matches!(result, Err(AutosarDataError::FilesetModificationForbidden)));
596
597 let text_before = file_a.serialize().unwrap();
599 model.remove_file(&file_b);
600 assert!(model.get_element_by_path("/Pkg2").is_none());
601 let text_after = file_a.serialize().unwrap();
602 assert_eq!(text_before, text_after);
603 }
604
605 #[test]
606 fn traits() {
607 let model = AutosarModel::new();
608 let file = model.create_file("filename", AutosarVersion::LATEST).unwrap();
609 let weak_file = file.downgrade();
610 let file_cloned = file.clone();
611 assert_eq!(file, file_cloned);
612 assert_eq!(format!("{file:#?}"), format!("{file_cloned:#?}"));
613 let mut hashset = HashSet::<ArxmlFile>::new();
614 hashset.insert(file);
615 let inserted = hashset.insert(file_cloned);
616 assert!(!inserted);
617
618 let weak_file_cloned = weak_file.clone();
619 assert_eq!(weak_file, weak_file_cloned);
620 assert_eq!(format!("{weak_file:#?}"), format!("{weak_file_cloned:#?}"));
621 let mut hashset = HashSet::<WeakArxmlFile>::new();
622 hashset.insert(weak_file);
623 let inserted = hashset.insert(weak_file_cloned);
624 assert!(!inserted);
625 }
626}