Skip to main content

nwnrs_gff/
merge.rs

1use crate::{GffField, GffResult, GffRoot, GffStruct, GffValue};
2
3/// Applies `edited` onto `target` while retaining provenance already present on
4/// matching parsed fields and structures.
5///
6/// # Errors
7///
8/// Currently infallible; returns `Ok(())` in all cases. The `Result` return
9/// type is reserved for future validation.
10pub 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}