assert_type_match/
structs.rs

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
use crate::args::Args;
use crate::utils::{create_member, extract_cfg_attrs, extract_field_args};
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{DataStruct, DeriveInput};

pub(crate) fn struct_assert(
    data: &DataStruct,
    input: &DeriveInput,
    args: &Args,
) -> syn::Result<TokenStream> {
    let ident = &input.ident;
    let foreign_ty = args.foreign_ty();
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let this = format_ident!("this");

    let fields = data
        .fields
        .iter()
        .enumerate()
        .filter_map(|(index, field)| {
            let member = create_member(field, index);
            let attrs = extract_cfg_attrs(&field.attrs);
            let field_args = match extract_field_args(&field.attrs) {
                Ok(args) => args,
                Err(err) => return Some(Err(err)),
            };

            if field_args.skip() {
                return None;
            }

            let value = if field_args.skip_type() {
                quote! { (|| { unreachable!() })() }
            } else {
                quote! { #this.#member }
            };

            Some(Ok(quote! {
                #( #attrs )*
                #member: #value,
            }))
        })
        .collect::<syn::Result<TokenStream>>()?;

    let (fn_name, this_ty) = if args.skip_types() {
        (
            format_ident!("__assert_untyped_fields_match"),
            foreign_ty.to_token_stream(),
        )
    } else {
        (
            format_ident!("__assert_typed_fields_match"),
            ident.to_token_stream(),
        )
    };

    Ok(quote! {
        fn #fn_name #impl_generics(#this: #this_ty #ty_generics) -> #foreign_ty #ty_generics #where_clause {
             #foreign_ty {
                #fields
            }
        }
    })
}

pub(crate) fn struct_from(
    data: &DataStruct,
    input: &DeriveInput,
    args: &Args,
) -> syn::Result<TokenStream> {
    let ident = &input.ident;
    let foreign_ty = args.foreign_ty();
    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

    let fields = data
        .fields
        .iter()
        .enumerate()
        .map(|(index, field)| {
            let member = create_member(field, index);
            let attrs = extract_cfg_attrs(&field.attrs);
            quote! {
                #( #attrs )*
                #member: this.#member.into(),
            }
        })
        .collect::<TokenStream>();

    Ok(quote! {
        impl #impl_generics ::core::convert::From<#ident #ty_generics> for #foreign_ty #ty_generics #where_clause {
            #[inline]
            fn from(this: #ident #ty_generics) -> Self {
                Self {
                    #fields
                }
            }
        }

        impl #impl_generics ::core::convert::From<#foreign_ty #ty_generics> for #ident #ty_generics #where_clause {
            #[inline]
            fn from(this: #foreign_ty #ty_generics) -> Self {
                Self {
                    #fields
                }
            }
        }
    })
}