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
use proc_macro2::TokenStream;

use super::ArgumentParameter;
use crate::{schema::InputValue, FieldArgument, FieldType, Ident, TypeIndex, TypePath};

/// A selection function for a field in our generated DSL
///
/// Each object in the schema will have one of these for each of it's
/// fields.  Calling this function will return a SelectionBuilder that
/// can be used to supply other arguments, and eventually build a
/// selection set.
#[derive(Debug)]
pub struct FieldSelector {
    pub rust_field_name: Ident,
    pub query_field_name: String,
    pub field_type: FieldType,
    pub type_lock: Ident,
    pub argument_structs_path: Ident,
    pub required_args: Vec<FieldArgument>,
    pub selection_builder: TypePath,
}

impl FieldSelector {
    pub fn for_field(
        name: &str,
        field_type: FieldType,
        type_lock: Ident,
        argument_structs_path: Ident,
        required_args: Vec<InputValue>,
        selection_builder: TypePath,
        type_index: &TypeIndex,
    ) -> FieldSelector {
        FieldSelector {
            rust_field_name: Ident::for_field(name),
            query_field_name: name.to_string(),
            field_type,
            type_lock,
            argument_structs_path,
            required_args: required_args
                .iter()
                .map(|v| FieldArgument::from_input_value(v, type_index))
                .collect(),
            selection_builder,
        }
    }
}

impl quote::ToTokens for FieldSelector {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        use quote::{quote, TokenStreamExt};

        let rust_field_name = &self.rust_field_name;

        let mut argument_defs = Vec::with_capacity(self.required_args.len());
        for arg in &self.required_args {
            argument_defs.push(
                ArgumentParameter::new(arg.name.clone(), arg.argument_type.clone())
                    .to_tokens(TypePath::empty())
                    .unwrap(),
            );
        }

        let argument_vals: Vec<_> = self
            .required_args
            .iter()
            .map(|a| {
                let name = &a.name;
                quote! { ::cynic::serde_json::to_value(&#name) }
            })
            .collect();
        let argument_strings: Vec<_> = self
            .required_args
            .iter()
            .map(|a| proc_macro2::Literal::string(&a.gql_name.to_string()))
            .collect();
        let argument_gql_types: Vec<_> = self
            .required_args
            .iter()
            .map(|a| a.gql_type.clone())
            .collect();

        let selection_builder = &self.selection_builder;

        tokens.append_all(quote! {
            pub fn #rust_field_name(
                #(#argument_defs, )*
            ) -> #selection_builder {
                #selection_builder::new(vec![
                    #(
                        ::cynic::Argument::new(
                            #argument_strings,
                            #argument_gql_types,
                            #argument_vals
                        ),
                    )*
                ])
            }
        })
    }
}