1
use proc_macro2::{Ident, Literal, TokenStream as TokenStream2};
2
use quote::{quote, ToTokens};
3
use syn::spanned::Spanned;
4
use syn::{Field, Fields};
5

            
6
use crate::utils::parse::{check_field_type, parse_field_type, UniplateField};
7

            
8
/// Generate the code to fill a field in a variant
9
32
fn get_fill(
10
32
    ft: &UniplateField,
11
32
    exprs_ident: &Ident,
12
32
    field_ident: &TokenStream2,
13
32
    root_ident: &Ident,
14
32
) -> TokenStream2 {
15
32
    if check_field_type(ft, root_ident) {
16
        // If the field or at least one of its children is a type we want to fill
17
28
        match ft {
18
            UniplateField::Identifier(_) => {
19
12
                return quote! {
20
12
                    #exprs_ident.remove(0) // If it is an identifier, take the next child from the list
21
12
                };
22
            }
23
8
            UniplateField::Box(_, subfield) => {
24
8
                let sf = subfield.as_ref();
25
8
                let sf_fill = get_fill(sf, exprs_ident, field_ident, root_ident);
26
8
                return quote! {
27
8
                    Box::new(#sf_fill) // If it is a box, generate the fill for the inner type and box it
28
8
                };
29
            }
30
5
            UniplateField::Vector(_, subfield) => {
31
5
                let sf = subfield.as_ref();
32
5
                let sf_fill = get_fill(sf, exprs_ident, field_ident, root_ident);
33
5
                return quote! { // The size is not known at compile time, so generate a loop to fill the vector (using the appropriate fill for the inner type)
34
5
                    {
35
5
                        let mut elems: Vec<_> = Vec::new();
36
5
                        for i in 0..#field_ident.len() { // The length of vectors must not change, so we can use the length of the field to determine how many children to take
37
5
                            elems.push(#sf_fill)
38
5
                        }
39
5
                        elems
40
5
                    }
41
5
                };
42
            }
43
3
            UniplateField::Tuple(_, sfs) => {
44
3
                let mut sf_fills: Vec<TokenStream2> = Vec::new();
45

            
46
6
                for (i, sf) in sfs.iter().enumerate() {
47
6
                    // Recursively generate the fill for each field in the tuple
48
6
                    let i_literal = Literal::usize_unsuffixed(i);
49
6
                    let sf_ident = quote! {
50
6
                        #field_ident.#i_literal
51
6
                    };
52
6
                    sf_fills.push(get_fill(sf, exprs_ident, &sf_ident, root_ident));
53
6
                }
54

            
55
3
                return quote! {
56
3
                    (#(#sf_fills,)*) // Wrap the fills in a tuple
57
3
                };
58
            }
59
            UniplateField::Array(_, _, _) => {
60
                unimplemented!("Arrays not currently supported") // ToDo support arrays
61
            }
62
            UniplateField::Unknown(_) => {}
63
        }
64
4
    }
65

            
66
4
    quote! {
67
4
        #field_ident.clone() // If the field is not a type we want to fill, just keep it
68
4
    }
69
32
}
70

            
71
/// Generate the code to clone a field in a variant
72
32
fn get_clone(
73
32
    ft: &UniplateField,
74
32
    field_ident: TokenStream2,
75
32
    root_ident: &Ident,
76
32
) -> Option<TokenStream2> {
77
32
    if check_field_type(ft, root_ident) {
78
        // If the field or at least one of its children is a type we want to clone
79
28
        match ft {
80
            UniplateField::Identifier(_) => {
81
12
                return Some(quote! {
82
12
                    vec![#field_ident.clone()] // If it is an identifier, clone it. We still need to wrap it in a vec to use .flatten() on the final list.
83
12
                });
84
            }
85
8
            UniplateField::Box(_, inner) => {
86
8
                let sf = inner.as_ref();
87
8
                let box_clone = quote! { // Generate the prefix for getting the inner type out of the box
88
8
                    #field_ident.as_ref().clone()
89
8
                };
90
8
                return get_clone(sf, box_clone, root_ident); // Then generate the clone for the inner type
91
            }
92
5
            UniplateField::Vector(_, inner) => {
93
5
                let sf = inner.as_ref();
94
5

            
95
5
                let sf_ident = Ident::new("sf", sf.span()).into_token_stream(); // Identity for the subfields
96
5
                let sf_clone = get_clone(sf, sf_ident, root_ident); // Clone for the subfields
97
5

            
98
5
                return Some(quote! {
99
5
                    #field_ident.iter().flat_map(|sf| #sf_clone).collect::<Vec<_>>() // If it is a vector, generate the clone for the inner type and flatten the list
100
5
                });
101
            }
102
3
            UniplateField::Tuple(_, sfs) => {
103
3
                let mut sf_clones: Vec<TokenStream2> = Vec::new();
104

            
105
6
                for (i, sf) in sfs.iter().enumerate() {
106
                    // Recursively generate the clone for each field in the tuple
107
6
                    let i_literal = Literal::usize_unsuffixed(i);
108
6
                    let sf_ident = quote! {
109
6
                        #field_ident.#i_literal
110
6
                    };
111
6
                    let sf_clone = get_clone(sf, sf_ident, root_ident);
112
6
                    match sf_clone {
113
1
                        None => {}
114
5
                        Some(sfc) => sf_clones.push(sfc),
115
                    }
116
                }
117

            
118
3
                return Some(quote! { // Clone the subfields into a vec and flatten
119
3
                    vec![#(#sf_clones,)*].iter().flatten().cloned().collect::<Vec<_>>()
120
3
                });
121
            }
122
            UniplateField::Array(_, _, _) => {
123
                // ToDo support arrays
124
                unimplemented!("Arrays not currently supported")
125
            }
126
            UniplateField::Unknown(_) => {} // Ignore unknown types
127
        }
128
4
    }
129

            
130
4
    None // If the field is not a type we want to clone, return None
131
32
}
132

            
133
/// Helper function to get the name of a field - if it has no name, use `field{idx}`
134
52
fn get_field_name(field: &Field, idx: usize) -> String {
135
52
    match &field.ident {
136
52
        None => format!("field{}", idx),
137
        Some(ident) => ident.to_string(),
138
    }
139
52
}
140

            
141
/// Generate the code to match the fields of a variant
142
18
pub fn generate_field_idents(fields: &Fields) -> Vec<TokenStream2> {
143
18
    return fields
144
18
        .iter()
145
18
        .enumerate()
146
26
        .map(|(idx, field)| {
147
26
            let field_name = get_field_name(field, idx);
148
26
            Ident::new(&field_name, field.ident.span()).into_token_stream()
149
26
        })
150
18
        .collect();
151
18
}
152

            
153
/// Generate the code to clone the fields of a variant
154
9
pub fn generate_field_clones(fields: &Fields, root_ident: &Ident) -> Vec<TokenStream2> {
155
9
    return fields
156
9
        .iter()
157
9
        .enumerate()
158
13
        .filter_map(|(idx, field)| {
159
13
            let field_name = get_field_name(field, idx);
160
13
            let field_type = parse_field_type(&field.ty);
161
13
            let field_ident = Ident::new(&field_name, field.ident.span()).into_token_stream();
162
13

            
163
13
            get_clone(&field_type, field_ident, root_ident)
164
13
        })
165
9
        .collect();
166
9
}
167

            
168
/// Generate the code to fill the fields of a variant
169
9
pub fn generate_field_fills(
170
9
    fields: &Fields,
171
9
    root_ident: &Ident,
172
9
    exprs_ident: &Ident,
173
9
) -> Vec<TokenStream2> {
174
9
    return fields
175
9
        .iter()
176
9
        .enumerate()
177
13
        .map(|(idx, field)| {
178
13
            let field_name = get_field_name(field, idx);
179
13
            let field_type = parse_field_type(&field.ty);
180
13
            let field_ident = Ident::new(&field_name, field.ident.span()).into_token_stream();
181
13

            
182
13
            get_fill(&field_type, exprs_ident, &field_ident, root_ident)
183
13
        })
184
9
        .collect();
185
9
}