From 7364187437740042365c0840fb57f1f6fa1d5154 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 27 Jun 2022 15:58:20 +0200 Subject: [PATCH] Impl `resolve::Value` and `resolve::ToInputValue` traits for enums [skip ci] --- juniper_codegen/src/graphql_enum/mod.rs | 204 +++++++++++++++++++++- juniper_codegen/src/graphql_scalar/mod.rs | 3 +- 2 files changed, 202 insertions(+), 5 deletions(-) diff --git a/juniper_codegen/src/graphql_enum/mod.rs b/juniper_codegen/src/graphql_enum/mod.rs index bebe05c2..2964b873 100644 --- a/juniper_codegen/src/graphql_enum/mod.rs +++ b/juniper_codegen/src/graphql_enum/mod.rs @@ -439,6 +439,16 @@ impl ToTokens for Definition { self.impl_to_input_value_tokens().to_tokens(into); self.impl_reflection_traits_tokens().to_tokens(into); //////////////////////////////////////////////////////////////////////// + //self.impl_resolve_type().to_tokens(into); + //self.impl_resolve_type_name().to_tokens(into); + self.impl_resolve_value().to_tokens(into); + self.impl_resolve_value_async().to_tokens(into); + self.impl_resolve_to_input_value().to_tokens(into); + //self.impl_resolve_input_value().to_tokens(into); + //self.impl_resolve_scalar_token().to_tokens(into); + //self.impl_graphql_input_type().to_tokens(into); + //self.impl_graphql_output_type().to_tokens(into); + //self.impl_graphql_enum().to_tokens(into); self.impl_reflect().to_tokens(into); } } @@ -602,6 +612,64 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::Value`] trait for this + /// [GraphQL enum][0]. + /// + /// [`resolve::Value`]: juniper::resolve::Value + /// [0]: https://spec.graphql.org/October2021#sec-Enums + fn impl_resolve_value(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = self.mix_type_info(generics); + let (cx, generics) = self.mix_context(generics); + let (sv, mut generics) = self.mix_scalar_value(generics); + generics.make_where_clause().predicates.push(parse_quote! { + #sv: From + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let variant_arms = self.values.iter().map(|v| { + let v_ident = &v.ident; + let v_name = &v.name; + + quote! { + Self::#v_ident => ::std::result::Result::Ok( + ::juniper::Value::<#sv>::scalar( + ::std::string::String::from(#v_name), + ), + ), + } + }); + let ignored_arm = self.has_ignored_variants.then(|| { + let err_msg = format!("Cannot resolve ignored variant of `{}` enum", self.ident); + + quote! { + _ => ::std::result::Result::Err( + ::juniper::FieldError::<#sv>::from(#err_msg), + ), + } + }); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + for #ty #where_clause + { + fn resolve_value( + &self, + _: Option<&[::juniper::Selection<'_, #sv>]>, + _: &#inf, + _: &::juniper::Executor<'_, '_, #cx, #sv>, + ) -> ::juniper::ExecutionResult<#sv> { + match self { + #( #variant_arms )* + #ignored_arm + } + } + } + } + } + /// Returns generated code implementing [`GraphQLValueAsync`] trait for this /// [GraphQL enum][0]. /// @@ -633,6 +701,48 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::ValueAsync`] trait for + /// this [GraphQL enum][0]. + /// + /// [`resolve::ValueAsync`]: juniper::resolve::ValueAsync + /// [0]: https://spec.graphql.org/October2021#sec-Enums + fn impl_resolve_value_async(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = self.mix_type_info(generics); + let (cx, generics) = self.mix_context(generics); + let (sv, mut generics) = self.mix_scalar_value(generics); + let preds = &mut generics.make_where_clause().predicates; + preds.push(parse_quote! { + Self: ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + }); + preds.push(parse_quote! { + #sv: Send + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::ValueAsync<#inf, #cx, #sv, #bh> + for #ty #where_clause + { + fn resolve_value_async<'__r>( + &'__r self, + sel_set: Option<&'__r [::juniper::Selection<'_, #sv>]>, + type_info: &'__r #inf, + executor: &'__r ::juniper::Executor<'_, '_, #cx, #sv>, + ) -> ::juniper::BoxFuture< + '__r, ::juniper::ExecutionResult<#sv>, + > { + let v = + > + ::resolve_value(self, sel_set, type_info, executor); + ::std::boxed::Box::pin(::juniper::futures::future::ready(v)) + } + } + } + } + /// Returns generated code implementing [`FromInputValue`] trait for this /// [GraphQL enum][0]. /// @@ -717,6 +827,53 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::ToInputValue`] trait for + /// this [GraphQL enum][0]. + /// + /// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue + /// [0]: https://spec.graphql.org/October2021#sec-Enums + fn impl_resolve_to_input_value(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (sv, mut generics) = self.mix_scalar_value(generics); + generics.make_where_clause().predicates.push(parse_quote! { + #sv: From + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let variant_arms = self.values.iter().map(|v| { + let v_ident = &v.ident; + let v_name = &v.name; + + quote! { + Self::#v_ident => ::juniper::graphql::InputValue::<#sv>::scalar( + ::std::string::String::from(#v_name), + ), + } + }); + let ignored_arm = self.has_ignored_variants.then(|| { + let err_msg = format!("Cannot resolve ignored variant of `{}` enum", self.ident); + + quote! { + _ => ::std::panic!(#err_msg), + } + }); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::ToInputValue<#sv, #bh> for #ty + #where_clause + { + fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> { + match self { + #( #variant_arms )* + #ignored_arm + } + } + } + } + } + /// Returns generated code implementing [`BaseType`], [`BaseSubTypes`] and /// [`WrappedType`] traits for this [GraphQL enum][0]. /// @@ -860,16 +1017,57 @@ impl Definition { /// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait /// implementation. - #[must_use] fn ty_and_generics(&self) -> (syn::Type, syn::Generics) { let generics = self.generics.clone(); - let ty = { let ident = &self.ident; let (_, ty_gen, _) = generics.split_for_impl(); parse_quote! { #ident#ty_gen } }; - (ty, generics) } + + /// Mixes a type info [`syn::GenericParam`] into the provided + /// [`syn::Generics`] and returns its [`syn::Ident`]. + fn mix_type_info(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) { + let ty = parse_quote! { __TypeInfo }; + generics.params.push(parse_quote! { #ty: ?Sized }); + (ty, generics) + } + + /// Mixes a context [`syn::GenericParam`] into the provided + /// [`syn::Generics`] and returns its [`syn::Ident`]. + fn mix_context(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) { + let ty = parse_quote! { __Context }; + generics.params.push(parse_quote! { #ty: ?Sized }); + (ty, generics) + } + + /// Mixes a [`ScalarValue`] [`syn::GenericParam`] into the provided + /// [`syn::Generics`] and returns it. + /// + /// [`ScalarValue`]: juniper::ScalarValue + fn mix_scalar_value(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) { + let sv = parse_quote! { __ScalarValue }; + generics.params.push(parse_quote! { #sv }); + (sv, generics) + } + + /// Mixes an [`InputValue`]'s lifetime [`syn::GenericParam`] into the + /// provided [`syn::Generics`] and returns it. + /// + /// [`InputValue`]: juniper::resolve::InputValue + fn mix_input_lifetime( + &self, + mut generics: syn::Generics, + sv: &syn::Ident, + ) -> (syn::GenericParam, syn::Generics) { + let lt: syn::GenericParam = parse_quote! { '__inp }; + generics.params.push(lt.clone()); + generics + .make_where_clause() + .predicates + .push(parse_quote! { #sv: #lt }); + (lt, generics) + } } diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index e4ebe806..c29924c9 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -1084,7 +1084,6 @@ impl Definition { #[must_use] fn ty_and_generics(&self) -> (syn::Type, syn::Generics) { let mut generics = self.generics.clone(); - let ty = { let ident = &self.ident; let (_, ty_gen, _) = generics.split_for_impl(); @@ -1248,7 +1247,7 @@ impl Methods { let into = sv.custom.is_some().then(|| { quote! { .map_scalar_value() } }); - quote! { Ok(#to_output(self)#into) } + quote! { ::std::result::Result::Ok(#to_output(self)#into) } } Self::Delegated { field, .. } => {