1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
pub(crate) fn generate(
    mut writer: impl std::io::Write,
    type_name: &str,
    generics: super::Generics<'_>,
    fields: &[super::Property<'_>],
    map_namespace: &impl crate::MapNamespace,
) -> Result<(), crate::Error> {
    let local = crate::map_namespace_local_to_string(map_namespace)?;

    let type_generics_type = generics.type_part.map(|part| format!("<{part}>")).unwrap_or_default();
    let type_generics_where = generics.where_part.map(|part| format!(" where {part}")).unwrap_or_default();

    let mut merge_body = String::new();
    for super::Property { field_name, merge_type, .. } in fields {
        generate_field(
            &mut merge_body,
            &local,
            &format!("&mut self.{field_name}"),
            &format!("other.{field_name}"),
            merge_type,
            8,
        )?;
    }

    writeln!(
        writer,
        include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/templates/struct_deep_merge.rs")),
        local = local,
        type_name = type_name,
        type_generics_type = type_generics_type,
        type_generics_where = type_generics_where,
        merge_body = merge_body,
    )?;

    Ok(())
}

fn generate_field(
    writer: &mut impl std::fmt::Write,
    local: &str,
    self_name: &str,
    other_name: &str,
    merge_type: &crate::swagger20::MergeType,
    indent: usize,
) -> Result<(), crate::Error> {
    const S: &str = "";

    match merge_type {
        crate::swagger20::MergeType::Default => writeln!(
            writer,
            "{S:indent$}{local}DeepMerge::merge_from({self_name}, {other_name});",
        )?,

        crate::swagger20::MergeType::List {
            strategy: crate::swagger20::KubernetesListType::Atomic,
            keys: _,
            item_merge_type: _,
        } => writeln!(
            writer,
            "{S:indent$}{local}merge_strategies::list::atomic({self_name}, {other_name});",
        )?,

        crate::swagger20::MergeType::List {
            strategy: crate::swagger20::KubernetesListType::Map,
            keys,
            item_merge_type,
        } => {
            writeln!(writer, "{S:indent$}{local}merge_strategies::list::map(")?;
            writeln!(writer, "{S:indent$}    {self_name},")?;
            writeln!(writer, "{S:indent$}    {other_name},")?;
            writeln!(
                writer,
                "{S:indent$}    &[{keys}],",
                keys = keys.iter().map(|k| format!("|lhs, rhs| lhs.{k} == rhs.{k}", k = crate::get_rust_ident(k))).collect::<Vec<_>>().join(", "),
            )?;
            writeln!(writer, "{S:indent$}    |current_item, other_item| {{")?;
            generate_field(writer, local, "current_item", "other_item", item_merge_type, indent + 8)?;
            writeln!(writer, "{S:indent$}    }},")?;
            writeln!(writer, "{S:indent$});")?;
        },

        crate::swagger20::MergeType::List {
            strategy: crate::swagger20::KubernetesListType::Set,
            keys: _,
            item_merge_type: _,
        } => writeln!(
            writer,
            "{S:indent$}{local}merge_strategies::list::set({self_name}, {other_name});",
        )?,

        crate::swagger20::MergeType::Map {
            strategy: crate::swagger20::KubernetesMapType::Granular,
            value_merge_type,
        } => {
            writeln!(
                writer,
                "{S:indent$}{local}merge_strategies::map::granular({self_name}, {other_name}, |current_item, other_item| {{",
            )?;
            generate_field(writer, local, "current_item", "other_item", value_merge_type, indent + 4)?;
            writeln!(writer, "{S:indent$}}});")?;
        },

        crate::swagger20::MergeType::Map {
            strategy: crate::swagger20::KubernetesMapType::Atomic,
            value_merge_type: _,
        } => writeln!(
            writer,
            "{S:indent$}{local}merge_strategies::map::atomic({self_name}, {other_name});",
        )?,
    }

    Ok(())
}