fack_codegen/
expand.rs

1//! Final code expansion for error definitions.
2
3use alloc::{format, vec::Vec};
4
5use proc_macro2::TokenStream;
6
7use quote::{ToTokens, quote};
8
9use syn::Ident;
10
11use super::{
12    Target,
13    common::{FieldRef, Format, ImportRoot, InlineOptions, Transparent},
14    enumerate::{Enumeration, Variant},
15    structure::{Structure, StructureOptions},
16};
17
18/// A struct that encapsulates the code generation for error definitions.
19pub struct Expand {}
20
21impl Expand {
22    /// Expand the [`Target`] into the appropriate error implementation.
23    #[inline]
24    pub fn target(target_value: Target) -> syn::Result<TokenStream> {
25        match target_value {
26            Target::Struct(structure) => Self::structure(structure),
27            Target::Enum(enumeration) => Self::enumeration(enumeration),
28        }
29    }
30}
31
32impl Expand {
33    /// Expand to the error implementation for the target [`Structure`].
34    pub fn structure(target_value: Structure) -> syn::Result<TokenStream> {
35        let Structure {
36            inline_opts,
37            root_import,
38            name_ident,
39            generics,
40            options,
41            field_list,
42        } = target_value;
43
44        let inline_expand = match inline_opts {
45            Some(InlineOptions::Neutral) => quote! { #[inline] },
46            Some(InlineOptions::Always) => quote! { #[inline(always)] },
47            Some(InlineOptions::Never) => quote! { #[inline(never)] },
48            None => quote! {},
49        };
50
51        let root_expand = root_import.map_or_else(|| quote! { ::core }, |ImportRoot(root)| root.to_token_stream());
52
53        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
54
55        let field_pat = field_list.pattern()?;
56
57        match options {
58            StructureOptions::Standalone {
59                source_field,
60                format_args: Format { format, format_args },
61            } => {
62                let source_expand = if let Some(field) = source_field {
63                    quote! { Some(&self.#field) }
64                } else {
65                    quote! { None }
66                };
67
68                Ok(quote! {
69                    #[automatically_derived]
70                    impl #impl_generics #root_expand::error::Error for #name_ident #ty_generics #where_clause {
71                        #inline_expand
72                        fn source(&self) -> Option<&(dyn #root_expand::error::Error + 'static)> {
73                            #source_expand
74                        }
75                    }
76
77                    #[automatically_derived]
78                    impl #impl_generics #root_expand::fmt::Display for #name_ident #ty_generics #where_clause {
79                        #inline_expand
80                        fn fmt(&self, f: &mut #root_expand::fmt::Formatter<'_>) -> #root_expand::fmt::Result {
81                            let &Self #field_pat = self;
82
83                            #root_expand::write!(f, #format, #format_args)
84                        }
85                    }
86                })
87            }
88            StructureOptions::Transparent(Transparent(target_field)) => {
89                let field_expand = quote! {
90                    self.#target_field
91                };
92
93                Ok(quote! {
94                    #[automatically_derived]
95                    impl #impl_generics #root_expand::error::Error for #name_ident #ty_generics #where_clause {
96                        #inline_expand
97                        fn source(&self) -> Option<&(dyn #root_expand::error::Error + 'static)> {
98                            #root_expand::error::Error::source(&#field_expand)
99                        }
100                    }
101
102                    #[automatically_derived]
103                    impl #impl_generics #root_expand::fmt::Display for #name_ident #ty_generics #where_clause {
104                        #inline_expand
105                        fn fmt(&self, f: &mut #root_expand::fmt::Formatter<'_>) -> #root_expand::fmt::Result {
106                            let &Self #field_pat = self;
107
108                            #root_expand::fmt::Display::fmt(&#field_expand, f)
109                        }
110                    }
111                })
112            }
113            StructureOptions::Forward {
114                field_ref,
115                field_type,
116                source_field,
117                format_args: Format { format, format_args },
118            } => {
119                let source_expand = if let Some(field) = source_field {
120                    quote! { Some(&self.#field) }
121                } else {
122                    quote! { None }
123                };
124
125                let from_expand = match field_ref {
126                    FieldRef::Named(ref field) => quote! {
127                        #[automatically_derived]
128                        impl #impl_generics From<#field_type> for #name_ident #ty_generics #where_clause {
129                            #inline_expand
130                            fn from(#field: #field_type) -> Self {
131                               Self { #field }
132                            }
133                        }
134                    },
135                    FieldRef::Indexed(..) => quote! {
136                        #[automatically_derived]
137                        impl #impl_generics From<#field_type> for #name_ident #ty_generics #where_clause {
138                            #inline_expand
139                            fn from(field: #field_type) -> Self {
140                                Self(field)
141                            }
142                        }
143                    },
144                };
145
146                Ok(quote! {
147                    #from_expand
148
149                    #[automatically_derived]
150                    impl #impl_generics #root_expand::error::Error for #name_ident #ty_generics #where_clause {
151                        #inline_expand
152                        fn source(&self) -> Option<&(dyn #root_expand::error::Error + 'static)> {
153                            #source_expand
154                        }
155                    }
156
157                    #[automatically_derived]
158                    impl #impl_generics #root_expand::fmt::Display for #name_ident #ty_generics #where_clause {
159                        #inline_expand
160                        fn fmt(&self, f: &mut #root_expand::fmt::Formatter<'_>) -> #root_expand::fmt::Result {
161                            let &Self #field_pat = self;
162
163                            #root_expand::write!(f, #format, #format_args)
164                        }
165                    }
166                })
167            }
168        }
169    }
170
171    /// Expand to the error implementation for the target [`Enumeration`].
172    pub fn enumeration(target_value: Enumeration) -> syn::Result<TokenStream> {
173        let Enumeration {
174            inline_opts,
175            root_import,
176            name_ident,
177            generics,
178            variant_list,
179        } = target_value;
180
181        let inline_expand = match inline_opts {
182            Some(InlineOptions::Neutral) => quote! { #[inline] },
183            Some(InlineOptions::Always) => quote! { #[inline(always)] },
184            Some(InlineOptions::Never) => quote! { #[inline(never)] },
185            None => quote! {},
186        };
187
188        let root_expand = root_import.map_or_else(|| quote! { ::core }, |ImportRoot(root)| root.to_token_stream());
189
190        let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
191
192        let mut source_expand = Vec::new();
193
194        let mut from_expand = Vec::new();
195
196        let mut display_expand = Vec::new();
197
198        for variant in variant_list {
199            match variant {
200                Variant::Struct {
201                    format: Format { format, format_args },
202                    variant_name,
203                    field_list,
204                    source_field,
205                } => {
206                    let field_pat = field_list.pattern()?;
207
208                    if let Some(source_field) = source_field {
209                        let field_expand = match source_field {
210                            FieldRef::Named(ref field) => quote! { self.#field },
211                            FieldRef::Indexed(index) => Ident::new(&format!("_{index}"), variant_name.span()).into_token_stream(),
212                        };
213
214                        source_expand.push(quote! {
215                            &Self::#variant_name #field_pat => Some(#field_expand),
216                        });
217                    } else {
218                        source_expand.push(quote! {
219                            &Self::#variant_name #field_pat => None,
220                        });
221                    }
222
223                    display_expand.push(quote! {
224                        &Self::#variant_name #field_pat  => #root_expand::write!(f, #format, #format_args),
225                    });
226                }
227                Variant::Unit {
228                    format: Format { format, format_args },
229                    variant_name,
230                } => {
231                    source_expand.push(quote! {
232                        &Self::#variant_name => None,
233                    });
234
235                    display_expand.push(quote! {
236                        &Self::#variant_name => #root_expand::write!(f, #format, #format_args),
237                    });
238                }
239                Variant::Transparent {
240                    transparent: Transparent(trans_field),
241                    variant_name,
242                    field_list,
243                } => {
244                    let trans_expand = match trans_field {
245                        FieldRef::Named(ref field) => quote! { #field },
246                        FieldRef::Indexed(index) => Ident::new(&format!("_{index}"), variant_name.span()).into_token_stream(),
247                    };
248
249                    let field_pat = field_list.pattern()?;
250
251                    source_expand.push(quote! {
252                        &Self::#variant_name #field_pat => #root_expand::error::Error::source(#trans_expand),
253                    });
254
255                    display_expand.push(quote! {
256                        &Self::#variant_name #field_pat => #root_expand::fmt::Display::fmt(#trans_expand, f),
257                    });
258                }
259                Variant::Forward {
260                    format: Format { format, format_args },
261                    variant_name,
262                    field_ref,
263                    field_type,
264                } => {
265                    let bare_field_name = quote!(#field_ref);
266
267                    source_expand.push(quote! {
268                        &Self::#variant_name { #bare_field_name: ref target_field }  => #root_expand::error::Error::source(target_field),
269                    });
270
271                    from_expand.push(match field_ref {
272                        FieldRef::Named(ref field) => quote! {
273                            #[automatically_derived]
274                            impl #impl_generics From<#field_type> for #name_ident #ty_generics #where_clause {
275                                #inline_expand
276                                fn from(#field: #field_type) -> Self {
277                                    Self::#variant_name { #field }
278                                }
279                            }
280                        },
281                        FieldRef::Indexed(..) => quote! {
282                            #[automatically_derived]
283                            impl #impl_generics From<#field_type> for #name_ident #ty_generics #where_clause {
284                                #inline_expand
285                                fn from(field: #field_type) -> Self {
286                                    Self::#variant_name(field)
287                                }
288                            }
289                        },
290                    });
291
292                    let replaced_field_name = match field_ref {
293                        FieldRef::Named(..) => None,
294                        FieldRef::Indexed(ref field) => Some({
295                            let ident = Ident::new(&format!("_{field}"), variant_name.span());
296
297                            quote! {
298                                : ref #ident
299                            }
300                        }),
301                    };
302
303                    display_expand.push(quote! {
304                        &Self::#variant_name { #bare_field_name #replaced_field_name }  => #root_expand::write!(f, #format, #format_args),
305                    });
306                }
307            }
308        }
309
310        Ok(quote! {
311            #(#from_expand)*
312
313            #[automatically_derived]
314            impl #impl_generics #root_expand::error::Error for #name_ident #ty_generics #where_clause {
315                #inline_expand
316                fn source(&self) -> Option<&(dyn #root_expand::error::Error + 'static)> {
317                    match self {
318                        #(#source_expand)*
319                    }
320                }
321            }
322
323            #[automatically_derived]
324            impl #impl_generics #root_expand::fmt::Display for #name_ident #ty_generics #where_clause {
325                #inline_expand
326                fn fmt(&self, f: &mut #root_expand::fmt::Formatter<'_>) -> #root_expand::fmt::Result {
327                    match self {
328                        #(#display_expand)*
329                    }
330                }
331            }
332        })
333    }
334}