dynamic-graphql-derive 0.10.2

Dynamic GraphQL schema macro
Documentation
use proc_macro2::TokenStream;
use quote::quote;

use crate::utils::common::CommonArg;
use crate::utils::crate_name::get_crate_name;
use crate::utils::impl_block::BaseFnArg;
use crate::utils::rename_rule::calc_arg_name;
use crate::utils::type_utils::get_owned_type;
use crate::utils::type_utils::get_value_type;
use crate::utils::type_utils::is_type_ref;

pub fn get_arg_ident(arg: &impl CommonArg) -> syn::Ident {
    syn::Ident::new(&format!("arg{}", arg.get_index()), arg.get_arg().span())
}

pub fn is_arg_ctx(arg: &impl CommonArg) -> bool {
    arg.is_marked_as_ctx()
        || matches!(arg.get_arg(), BaseFnArg::Typed(typed) if typed.ident == "ctx" || typed.ident == "_ctx")
}

pub fn get_self_arg_usage(arg: &impl CommonArg) -> darling::Result<TokenStream> {
    let arg_ident = get_arg_ident(arg);
    Ok(quote!(#arg_ident,))
}

pub fn get_typed_arg_usage(arg: &impl CommonArg) -> darling::Result<TokenStream> {
    let arg_ident = get_arg_ident(arg);
    let BaseFnArg::Typed(typed) = arg.get_arg() else {
        unreachable!("Expected typed argument");
    };
    let is_ctx = is_arg_ctx(arg);
    let is_owned = !is_type_ref(&typed.ty);
    if is_ctx || is_owned {
        Ok(quote!(#arg_ident,))
    } else {
        Ok(quote!(&#arg_ident,))
    }
}

pub fn get_argument_definition(arg: &impl CommonArg) -> darling::Result<TokenStream> {
    if is_arg_ctx(arg) {
        return Ok(quote!());
    }
    let BaseFnArg::Typed(typed) = arg.get_arg() else {
        return Ok(quote!());
    };
    let crate_name = get_crate_name();
    let arg_name = calc_arg_name(
        arg.get_name(),
        &typed.ident.to_string(),
        arg.get_arg_rename_rule(),
    );
    let arg_type = get_owned_type(&typed.ty);

    let description = match arg.get_doc()? {
        Some(doc) if !doc.is_empty() => quote! {
            let arg = arg.description(#doc);
        },
        _ => quote!(),
    };

    Ok(quote! {
        let arg = #crate_name::dynamic::InputValue::new(#arg_name, <#arg_type as #crate_name::internal::GetInputTypeRef>::get_input_type_ref());
        #description
        let field = field.argument(arg);
    })
}

pub fn get_argument_definitions(args: &[impl CommonArg]) -> darling::Result<TokenStream> {
    args.iter().map(get_argument_definition).collect()
}

pub fn get_typed_arg_definition(arg: &impl CommonArg) -> darling::Result<TokenStream> {
    let BaseFnArg::Typed(typed) = arg.get_arg() else {
        unreachable!("Expected typed argument");
    };
    let crate_name = get_crate_name();
    let arg_ident = get_arg_ident(arg);
    if is_arg_ctx(arg) {
        Ok(quote! {
            let #arg_ident = &ctx;
        })
    } else {
        let arg_name = calc_arg_name(
            arg.get_name(),
            &typed.ident.to_string(),
            arg.get_arg_rename_rule(),
        );
        let value_type = get_value_type(&typed.ty);
        match value_type {
            None => Ok(quote! {
                let #arg_ident = #crate_name::internal::FromValue::from_value(ctx.args.try_get(#arg_name)).map_err(|e| e.into_arg_error(#arg_name))?;
            }),
            Some(ty) => Ok(quote! {
                let #arg_ident: #ty = #crate_name::internal::FromValue::from_value(ctx.args.try_get(#arg_name)).map_err(|e| e.into_arg_error(#arg_name))?;
            }),
        }
    }
}