autosar_data/
arxmlfile.rs

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    /// Get the filename of this `ArxmlFile`
17    ///
18    /// # Example
19    ///
20    /// ```
21    /// # use autosar_data::*;
22    /// # let model = AutosarModel::new();
23    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
24    /// println!("filename is : {}", file.filename().display());
25    /// ```
26    #[must_use]
27    pub fn filename(&self) -> PathBuf {
28        self.0.read().filename.clone()
29    }
30
31    /// Get the [`AutosarVersion`] of the file
32    ///
33    /// # Example
34    ///
35    /// ```
36    /// # use autosar_data::*;
37    /// # let model = AutosarModel::new();
38    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
39    /// let version = file.version();
40    /// ```
41    #[must_use]
42    pub fn version(&self) -> AutosarVersion {
43        self.0.read().version
44    }
45
46    /// Set the [`AutosarVersion`] of the file
47    ///
48    /// The compatibility of the data in the file with the new version will be checked before setting the version.
49    /// The compatibility check can also be performed manually using the function `check_version_compatibility()`.
50    ///
51    /// If the data is compatible, then the version is set, otherwise an error is raised.
52    ///
53    /// # Example
54    ///
55    /// ```
56    /// # use autosar_data::*;
57    /// # let model = AutosarModel::new();
58    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
59    /// file.set_version(AutosarVersion::Autosar_00050);
60    /// ```
61    ///
62    /// # Errors
63    ///
64    ///  - [`AutosarDataError::VersionIncompatibleData`] the existing data is not compatible with the new version
65    ///
66    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    /// Check if the elements and attributes in this file are compatible with some `target_version`
78    ///
79    /// All elements and their attributes will be evaluated against the target version according to the specification.
80    /// The output is a list of incompatible elements
81    ///
82    /// # Example
83    ///
84    /// ```
85    /// # use autosar_data::*;
86    /// # let model = AutosarModel::new();
87    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
88    /// let (error_list, compat_mask) = file.check_version_compatibility(AutosarVersion::Autosar_00050);
89    /// ```
90    #[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    /// Set the filename of this arxml filename
102    ///
103    /// This will not rename any existing file on disk, but the new filename will be used when writing the data.
104    ///
105    /// # Example
106    ///
107    /// ```
108    /// # use std::path::Path;
109    /// # use autosar_data::*;
110    /// # let model = AutosarModel::new();
111    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
112    /// file.set_filename("foo.arxml");
113    /// // or
114    /// file.set_filename(&Path::new("bar.arxml"));
115    /// ```
116    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    /// Get a reference to the [`AutosarModel`] object that contains this file
135    ///
136    /// # Example
137    ///
138    /// ```
139    /// # use autosar_data::*;
140    /// # fn main() -> Result<(), AutosarDataError> {
141    /// let model = AutosarModel::new();
142    /// let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
143    /// let m2 = file.model()?;
144    /// assert_eq!(model, m2);
145    /// # Ok(())
146    /// # }
147    /// ```
148    ///
149    /// # Errors
150    ///
151    /// [`AutosarDataError::ItemDeleted`]: The model is no longer valid
152    ///
153    pub fn model(&self) -> Result<AutosarModel, AutosarDataError> {
154        let locked_file = self.0.write();
155        // This reference must always be valid, so it is an error if upgrade() fails
156        locked_file.model.upgrade().ok_or(AutosarDataError::ItemDeleted)
157    }
158
159    /// Create a depth-first search iterator over all [Element]s in this file
160    ///
161    /// In a multi-file model it will not return any elements from other files.
162    ///
163    /// # Example
164    ///
165    /// ```
166    /// # use autosar_data::*;
167    /// # let model = AutosarModel::new();
168    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
169    /// for (depth, elem) in file.elements_dfs() {
170    ///     // ...
171    /// }
172    /// ```
173    #[must_use]
174    pub fn elements_dfs(&self) -> ArxmlFileElementsDfsIterator {
175        ArxmlFileElementsDfsIterator::new(self, 0)
176    }
177
178    /// Create a depth first iterator over all [Element]s in this file, up to a maximum depth
179    ///
180    /// In a multi-file model it will not return any elements from other files.
181    ///
182    /// # Example
183    ///
184    /// ```
185    /// # use autosar_data::*;
186    /// # let model = AutosarModel::new();
187    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
188    /// # let element = model.root_element();
189    /// # element.create_sub_element(ElementName::ArPackages).unwrap();
190    /// # let sub_elem = element.get_sub_element(ElementName::ArPackages).unwrap();
191    /// # sub_elem.create_named_sub_element(ElementName::ArPackage, "test2").unwrap();
192    /// for (depth, elem) in file.elements_dfs_with_max_depth(1) {
193    ///     assert!(depth <= 1);
194    ///     // ...
195    /// }
196    /// ```
197    #[must_use]
198    pub fn elements_dfs_with_max_depth(&self, max_depth: usize) -> ArxmlFileElementsDfsIterator {
199        ArxmlFileElementsDfsIterator::new(self, max_depth)
200    }
201
202    /// Serialize the content of the file to a String
203    ///
204    /// # Example
205    ///
206    /// ```
207    /// # use autosar_data::*;
208    /// # let model = AutosarModel::new();
209    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
210    /// let text = file.serialize();
211    /// ```
212    ///
213    /// # Errors
214    ///
215    /// [`AutosarDataError::ItemDeleted`]: The model is no longer valid
216    /// [`AutosarDataError::EmptyFile`]: The file is empty and cannot be serialized
217    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    /// Return the standalone attribute from the xml header
239    ///
240    /// Some tools set headers that include the standalone attribute.
241    /// This attribute appears to be meaningless for arxml files.
242    ///
243    /// It is preserved nontheless and can be retrieved with this function.
244    ///
245    /// # Example
246    ///
247    /// ```
248    /// # use autosar_data::*;
249    /// let model = AutosarModel::new();
250    /// let file_text = r#"<?xml version="1.0" encoding="utf-8" standalone="no"?>
251    /// <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">
252    /// </AUTOSAR>"#.as_bytes();
253    /// let (file, _warnings) = model.load_buffer(file_text, "filename.arxml", true).unwrap();
254    /// assert_eq!(file.xml_standalone(), Some(false));
255    /// ```
256    #[must_use]
257    pub fn xml_standalone(&self) -> Option<bool> {
258        self.0.read().xml_standalone
259    }
260
261    /// Create a weak reference to this `ArxmlFile`
262    ///
263    /// A weak reference can be stored without preventing the file from being deallocated.
264    /// The weak reference has to be upgraded in order to be used, which can fail if the file no longer exists.
265    ///
266    /// See the documentation for [Arc]
267    ///
268    /// # Example
269    ///
270    /// ```
271    /// # use autosar_data::*;
272    /// # let model = AutosarModel::new();
273    /// # let file = model.create_file("test", AutosarVersion::Autosar_00050).unwrap();
274    /// let weak_file = file.downgrade();
275    /// ```
276    #[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    /// try to get a strong reference to the [`ArxmlFile`]
316    ///
317    /// This succeeds if the `ArxmlFile` still has any other strong reference to it, otherwise None is returned
318    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); // 3 references are: AutosarModel, file, file2
400        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        // two files are created, both contain AUTOSAR. Then a child element of AUTOSAR is created, which is present in both files
513        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        // el_a_packages is part of both file_a and file_b, because both files automatically contain the
521        // root AUTOSAR element, and AR_PACKAGES inherits this
522        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        // one file is created, which contains AUTOSAR. Then a child element is created, which is automatically part of this file.
530        // then a second file is created, but the element is NOT part of the new file
531        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        // el_a_packages is only part of file_a
539        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        // a file is created with multiple sub elements. A pat of this hierarchy is added to a second file
547        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())); // el_pkg1 is part of file_a
562        assert!(!fs.contains(&file_b.downgrade())); // el_pkg1 is not part of file_b
563        let (_, fs) = el_pkg2.file_membership().unwrap();
564        assert!(fs.contains(&file_a.downgrade())); // el_pkg2 is part of file_a
565        assert!(!fs.contains(&file_b.downgrade())); // el_pkg2 is not part of file_b
566
567        // add el_pkg2 to file_b
568        el_pkg2.add_to_file(&file_b).unwrap();
569        let (_, fs) = el_pkg1.file_membership().unwrap();
570        assert!(fs.contains(&file_a.downgrade())); // el_pkg1 is part of file_a
571        assert!(!fs.contains(&file_b.downgrade())); // el_pkg1 is not part of file_b
572        let (_, fs) = el_pkg2.file_membership().unwrap();
573        assert!(fs.contains(&file_a.downgrade())); // el_pkg2 is part of file_a
574        assert!(fs.contains(&file_b.downgrade())); // el_pkg2 is part of file_b
575
576        // el_ar_packages was automatically added to file_b, in order to add el_pkg2
577        let (_, fs) = el_ar_packages.file_membership().unwrap();
578        assert!(fs.contains(&file_a.downgrade())); // el_ar_packages is part of file_a
579        assert!(fs.contains(&file_b.downgrade())); // el_ar_packages is part of file_b
580
581        // remove el_pkg2 from file_a
582        let (_, fs) = el_pkg2.file_membership().unwrap();
583        assert!(fs.contains(&file_a.downgrade())); // el_pkg2 is part of file_a
584        assert!(fs.contains(&file_b.downgrade())); // el_pkg2 is part of file_b
585        el_pkg2.remove_from_file(&file_a).unwrap();
586        let (_, fs) = el_pkg2.file_membership().unwrap();
587        assert!(!fs.contains(&file_a.downgrade())); // el_pkg2 is part of file_a
588        assert!(fs.contains(&file_b.downgrade())); // el_pkg2 is part of file_b
589
590        // add_to_file / remove_from_file cannot be called on all elements
591        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        // serializing a single file
598        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}