1use crate::{GffField, GffResult, GffRoot, GffStruct, GffValue};
2
3pub fn merge_root_preserving_provenance(target: &mut GffRoot, edited: &GffRoot) -> GffResult<()> {
11 target.file_type.clone_from(&edited.file_type);
12 target.file_version.clone_from(&edited.file_version);
13 merge_struct_preserving_provenance(&mut target.root, &edited.root);
14 Ok(())
15}
16
17fn merge_struct_preserving_provenance(target: &mut GffStruct, edited: &GffStruct) {
18 target.id = edited.id;
19
20 let mut existing_fields = std::mem::take(&mut target.fields);
21 let mut merged_fields = Vec::with_capacity(edited.fields().len());
22
23 for (label, edited_field) in edited.fields() {
24 if let Some(position) = existing_fields
25 .iter()
26 .position(|(existing_label, _)| existing_label == label)
27 {
28 let (_, mut existing_field) = existing_fields.remove(position);
29 merge_field_preserving_provenance(&mut existing_field, edited_field);
30 merged_fields.push((label.clone(), existing_field));
31 } else {
32 merged_fields.push((label.clone(), edited_field.clone()));
33 }
34 }
35
36 target.fields = merged_fields;
37 if let Some(provenance) = &mut target.provenance {
38 provenance.field_labels = target
39 .fields
40 .iter()
41 .map(|(label, _)| label.clone())
42 .collect();
43 }
44}
45
46fn merge_field_preserving_provenance(target: &mut GffField, edited: &GffField) {
47 if target.kind() != edited.kind() {
48 *target = edited.clone();
49 return;
50 }
51
52 merge_value_preserving_provenance(&mut target.value, edited.value());
53}
54
55fn merge_value_preserving_provenance(target: &mut GffValue, edited: &GffValue) {
56 match edited {
57 GffValue::Struct(edited_struct) => {
58 if let GffValue::Struct(target_struct) = target {
59 merge_struct_preserving_provenance(target_struct, edited_struct);
60 } else {
61 *target = edited.clone();
62 }
63 }
64 GffValue::List(edited_list) => {
65 if let GffValue::List(target_list) = target {
66 let existing = std::mem::take(target_list);
67 let mut merged = Vec::with_capacity(edited_list.len());
68
69 for (edited_struct, maybe_existing_struct) in edited_list.iter().zip(
70 existing
71 .into_iter()
72 .map(Some)
73 .chain(std::iter::repeat(None)),
74 ) {
75 if let Some(mut existing_struct) = maybe_existing_struct {
76 merge_struct_preserving_provenance(&mut existing_struct, edited_struct);
77 merged.push(existing_struct);
78 } else {
79 merged.push(edited_struct.clone());
80 }
81 }
82
83 *target_list = merged;
84 } else {
85 *target = edited.clone();
86 }
87 }
88 _ => *target = edited.clone(),
89 }
90}