inter_struct_codegen/generate/merge/
owned.rs

1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use syn::Field;
4
5use crate::error::*;
6use crate::generate::field::*;
7use crate::generate::types::*;
8use crate::generate::Parameters;
9
10/// Generate the implementation of [inter_struct::merge::StructMerge] for given structs.
11pub(crate) fn impl_owned(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream {
12    let mut functions_tokens = TokenStream::new();
13
14    // Add `merge` impl.
15    let stream = merge(params, fields);
16    functions_tokens.extend(vec![stream]);
17
18    // Surround functions with `impl` block.
19    let src_ident = &params.src_struct.ident;
20    let target_path = &params.target_path;
21    quote! {
22        impl inter_struct::merge::StructMergeInto<#target_path> for #src_ident {
23            #functions_tokens
24        }
25    }
26}
27
28/// Generate the [inter_struct::merge::StructMerge::merge] function for the given structs.
29fn merge(params: &Parameters, fields: Vec<(Field, Field)>) -> TokenStream {
30    let mut merge_code = TokenStream::new();
31    for (src_field, target_field) in fields {
32        let src_field_ident = src_field.ident;
33        let target_field_ident = target_field.ident;
34
35        // Find out, whether the fields are optional or not.
36        let src_field_type = match determine_field_type(src_field.ty) {
37            Ok(field) => field,
38            Err(err) => {
39                merge_code.extend(vec![err]);
40                continue;
41            }
42        };
43        let target_field_type = match determine_field_type(target_field.ty) {
44            Ok(field) => field,
45            Err(err) => {
46                merge_code.extend(vec![err]);
47                continue;
48            }
49        };
50
51        let snippet = match (src_field_type, target_field_type) {
52            // Both fields have the same type
53            (FieldType::Normal(src_type), FieldType::Normal(target_type)) => {
54                equal_type_or_err!(
55                    src_type,
56                    target_type,
57                    quote! {
58                        target.#target_field_ident = self.#src_field_ident;
59                    }
60                )
61            }
62            // The src is optional and needs to be `Some(T)` to be merged.
63            (
64                FieldType::Optional {
65                    inner: src_type, ..
66                },
67                FieldType::Normal(target_type),
68            ) => {
69                equal_type_or_err!(
70                    src_type,
71                    target_type,
72                    quote! {
73                        if let Some(value) = self.#src_field_ident {
74                            target.#target_field_ident = value;
75                        }
76                    }
77                )
78            }
79            // The target is optional and needs to be wrapped in `Some(T)` to be merged.
80            (
81                FieldType::Normal(src_type),
82                FieldType::Optional {
83                    inner: target_type, ..
84                },
85            ) => {
86                equal_type_or_err!(
87                    src_type,
88                    target_type,
89                    quote! {
90                        target.#target_field_ident = Some(self.#src_field_ident);
91                    }
92                )
93            }
94            // Both fields are optional. It can now be either of these:
95            // - (Option<T>, Option<T>)
96            // - (Option<Option<T>>, Option<T>)
97            // - (Option<T>, Option<Option<T>>)
98            (
99                FieldType::Optional {
100                    inner: inner_src_type,
101                    outer: outer_src_type,
102                },
103                FieldType::Optional {
104                    inner: inner_target_type,
105                    outer: outer_target_type,
106                },
107            ) => {
108                // Handling the (Option<T>, Option<T>) case
109                if is_equal_type(&inner_src_type, &inner_target_type) {
110                    equal_type_or_err!(
111                        inner_src_type,
112                        inner_target_type,
113                        quote! {
114                            target.#target_field_ident = self.#src_field_ident;
115                        }
116                    )
117                // Handling the (Option<Option<<T>>, Option<T>) case
118                } else if is_equal_type(&inner_src_type, &outer_target_type) {
119                    equal_type_or_err!(
120                        inner_src_type,
121                        outer_target_type,
122                        quote! {
123                            if let Some(value) = self.#src_field_ident {
124                                target.#target_field_ident = value;
125                            }
126                        }
127                    )
128                // Handling the (Option<<T>, Option<Option<T>)> case
129                } else {
130                    equal_type_or_err!(
131                        outer_src_type,
132                        inner_target_type,
133                        quote! {
134                            target.#target_field_ident = Some(self.#src_field_ident);
135                        }
136                    )
137                }
138            }
139            // Skip anything where either of the fields are invalid
140            (FieldType::Invalid, _) | (_, FieldType::Invalid) => continue,
141        };
142
143        merge_code.extend(vec![snippet]);
144    }
145
146    let merge_code = merge_code.to_token_stream();
147
148    let target_path = &params.target_path;
149    quote! {
150        fn merge_into(self, target: &mut #target_path) {
151            #merge_code
152        }
153    }
154}