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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use crate::args;
use crate::utils::{build_value_repr, check_reserved_name, get_crate_name};
use inflector::Inflector;
use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Error, Result};

pub fn generate(object_args: &args::InputObject, input: &DeriveInput) -> Result<TokenStream> {
    let crate_name = get_crate_name(object_args.internal);
    let ident = &input.ident;
    let attrs = &input.attrs;
    let vis = &input.vis;
    let s = match &input.data {
        Data::Struct(s) => s,
        _ => return Err(Error::new_spanned(input, "It should be a struct.")),
    };

    let mut struct_fields = Vec::new();
    for field in &s.fields {
        let vis = &field.vis;
        let ty = &field.ty;
        let ident = &field.ident;
        let attrs = field
            .attrs
            .iter()
            .filter(|attr| !attr.path.is_ident("field"))
            .collect::<Vec<_>>();
        struct_fields.push(quote! {
            #(#attrs)*
            #vis #ident: #ty
        });
    }
    let new_struct = quote! {
        #(#attrs)*
        #vis struct #ident {
            #(#struct_fields),*
        }
    };

    let gql_typename = object_args
        .name
        .clone()
        .unwrap_or_else(|| ident.to_string());
    check_reserved_name(&gql_typename, object_args.internal)?;

    let desc = object_args
        .desc
        .as_ref()
        .map(|s| quote! {Some(#s)})
        .unwrap_or_else(|| quote! {None});

    let mut get_fields = Vec::new();
    let mut fields = Vec::new();
    let mut schema_fields = Vec::new();

    for field in &s.fields {
        let field_args = args::InputField::parse(&crate_name, &field.attrs)?;
        let ident = field.ident.as_ref().unwrap();
        let ty = &field.ty;
        let validator = &field_args.validator;
        let name = field_args
            .name
            .unwrap_or_else(|| ident.to_string().to_camel_case());
        let desc = field_args
            .desc
            .as_ref()
            .map(|s| quote! {Some(#s)})
            .unwrap_or_else(|| quote! {None});
        let default = field_args
            .default
            .as_ref()
            .map(|v| {
                let s = v.to_string();
                quote! {Some(#s)}
            })
            .unwrap_or_else(|| quote! {None});

        if let Some(default) = &field_args.default {
            let default_repr = build_value_repr(&crate_name, default);
            get_fields.push(quote! {
                let #ident:#ty = {
                    match obj.get(#name) {
                        Some(value) => #crate_name::InputValueType::parse(value)?,
                        None => {
                            let default = #default_repr;
                            #crate_name::InputValueType::parse(&default)?
                        }
                    }
                };
            });
        } else {
            get_fields.push(quote! {
                let #ident:#ty = #crate_name::InputValueType::parse(obj.get(#name).unwrap_or(&#crate_name::Value::Null))?;
            });
        }

        fields.push(ident);
        schema_fields.push(quote! {
            fields.insert(#name.to_string(), #crate_name::registry::InputValue {
                name: #name,
                description: #desc,
                ty: <#ty as #crate_name::Type>::create_type_info(registry),
                default_value: #default,
                validator: #validator,
            });
        })
    }

    let expanded = quote! {
        #new_struct

        impl #crate_name::Type for #ident {
            fn type_name() -> std::borrow::Cow<'static, str> {
                std::borrow::Cow::Borrowed(#gql_typename)
            }

            fn create_type_info(registry: &mut #crate_name::registry::Registry) -> String {
                registry.create_type::<Self, _>(|registry| #crate_name::registry::Type::InputObject {
                    name: #gql_typename.to_string(),
                    description: #desc,
                    input_fields: {
                        let mut fields = std::collections::HashMap::new();
                        #(#schema_fields)*
                        fields
                    }
                })
            }
        }

        impl #crate_name::InputValueType for #ident {
            fn parse(value: &#crate_name::Value) -> Option<Self> {
                use #crate_name::Type;

                if let #crate_name::Value::Object(obj) = value {
                    #(#get_fields)*
                    Some(Self { #(#fields),* })
                } else {
                    None
                }
            }
        }

        impl #crate_name::InputObjectType for #ident {}
    };
    Ok(expanded.into())
}