Panics when the same Rust type has the same name. #880

This commit is contained in:
Sunli 2022-03-30 20:54:49 +08:00
parent aec1f27ac6
commit a62eb6f699
22 changed files with 174 additions and 72 deletions

View File

@ -130,7 +130,7 @@ pub fn generate(enum_args: &args::Enum) -> GeneratorResult<TokenStream> {
}
fn __create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_input_type::<Self, _>(|registry| {
registry.create_input_type::<Self, _>(#crate_name::registry::MetaTypeId::Enum, |registry| {
#crate_name::registry::MetaType::Enum {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
description: #desc,

View File

@ -213,7 +213,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_input_type::<Self, _>(|registry| #crate_name::registry::MetaType::InputObject {
registry.create_input_type::<Self, _>(#crate_name::registry::MetaTypeId::InputObject, |registry| #crate_name::registry::MetaType::InputObject {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
description: #desc,
input_fields: {
@ -260,7 +260,7 @@ pub fn generate(object_args: &args::InputObject) -> GeneratorResult<TokenStream>
#[allow(clippy::all, clippy::pedantic)]
impl #impl_generics #ident #ty_generics #where_clause {
fn __internal_create_type_info(registry: &mut #crate_name::registry::Registry, name: &str) -> ::std::string::String where Self: #crate_name::InputType {
registry.create_input_type::<Self, _>(|registry| #crate_name::registry::MetaType::InputObject {
registry.create_input_type::<Self, _>(#crate_name::registry::MetaTypeId::InputObject, |registry| #crate_name::registry::MetaType::InputObject {
name: ::std::borrow::ToOwned::to_owned(name),
description: #desc,
input_fields: {

View File

@ -374,7 +374,7 @@ pub fn generate(interface_args: &args::Interface) -> GeneratorResult<TokenStream
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_output_type::<Self, _>(|registry| {
registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Interface, |registry| {
#(#registry_types)*
#crate_name::registry::MetaType::Interface {

View File

@ -82,7 +82,7 @@ pub fn generate(object_args: &args::MergedObject) -> GeneratorResult<TokenStream
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_output_type::<Self, _>(|registry| {
registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Object, |registry| {
let mut fields = ::std::default::Default::default();
let mut cache_control = ::std::default::Default::default();

View File

@ -44,7 +44,7 @@ pub fn generate(newtype_args: &args::NewType) -> GeneratorResult<TokenStream> {
};
quote! {
registry.create_input_type::<#ident, _>(|_| #crate_name::registry::MetaType::Scalar {
registry.create_input_type::<#ident, _>(#crate_name::registry::MetaTypeId::Scalar, |_| #crate_name::registry::MetaType::Scalar {
name: ::std::borrow::ToOwned::to_owned(#name),
description: #desc,
is_valid: |value| <#ident as #crate_name::ScalarType>::is_valid(value),

View File

@ -653,7 +653,7 @@ pub fn generate(
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
let ty = registry.create_output_type::<Self, _>(|registry| #crate_name::registry::MetaType::Object {
let ty = registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Object, |registry| #crate_name::registry::MetaType::Object {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
description: #desc,
fields: {
@ -692,7 +692,7 @@ pub fn generate(
impl #impl_generics #self_ty #where_clause {
fn __internal_create_type_info(registry: &mut #crate_name::registry::Registry, name: &str) -> ::std::string::String where Self: #crate_name::OutputType {
let ty = registry.create_output_type::<Self, _>(|registry| #crate_name::registry::MetaType::Object {
let ty = registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Object, |registry| #crate_name::registry::MetaType::Object {
name: ::std::borrow::ToOwned::to_owned(name),
description: #desc,
fields: {

View File

@ -131,7 +131,7 @@ pub fn generate(object_args: &args::OneofObject) -> GeneratorResult<TokenStream>
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_input_type::<Self, _>(|registry| #crate_name::registry::MetaType::InputObject {
registry.create_input_type::<Self, _>(#crate_name::registry::MetaTypeId::InputObject, |registry| #crate_name::registry::MetaType::InputObject {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
description: #desc,
input_fields: {
@ -181,7 +181,7 @@ pub fn generate(object_args: &args::OneofObject) -> GeneratorResult<TokenStream>
#[allow(clippy::all, clippy::pedantic)]
impl #impl_generics #ident #ty_generics #where_clause {
fn __internal_create_type_info(registry: &mut #crate_name::registry::Registry, name: &str) -> ::std::string::String where Self: #crate_name::InputType {
registry.create_input_type::<Self, _>(|registry| #crate_name::registry::MetaType::InputObject {
registry.create_input_type::<Self, _>(#crate_name::registry::MetaTypeId::InputObject, |registry| #crate_name::registry::MetaType::InputObject {
name: ::std::borrow::ToOwned::to_owned(name),
description: #desc,
input_fields: {

View File

@ -47,7 +47,7 @@ pub fn generate(
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_input_type::<#self_ty, _>(|_| #crate_name::registry::MetaType::Scalar {
registry.create_input_type::<#self_ty, _>(#crate_name::registry::MetaTypeId::Scalar, |_| #crate_name::registry::MetaType::Scalar {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
description: #desc,
is_valid: |value| <#self_ty as #crate_name::ScalarType>::is_valid(value),
@ -77,7 +77,7 @@ pub fn generate(
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_output_type::<#self_ty, _>(|_| #crate_name::registry::MetaType::Scalar {
registry.create_output_type::<#self_ty, _>(#crate_name::registry::MetaTypeId::Scalar, |_| #crate_name::registry::MetaType::Scalar {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
description: #desc,
is_valid: |value| <#self_ty as #crate_name::ScalarType>::is_valid(value),

View File

@ -311,7 +311,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_output_type::<Self, _>(|registry| #crate_name::registry::MetaType::Object {
registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Object, |registry| #crate_name::registry::MetaType::Object {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
description: #desc,
fields: {
@ -375,7 +375,7 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
name: &str,
complex_fields: #crate_name::indexmap::IndexMap<::std::string::String, #crate_name::registry::MetaField>,
) -> ::std::string::String where Self: #crate_name::OutputType {
registry.create_output_type::<Self, _>(|registry| #crate_name::registry::MetaType::Object {
registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Object, |registry| #crate_name::registry::MetaType::Object {
name: ::std::borrow::ToOwned::to_owned(name),
description: #desc,
fields: {

View File

@ -172,7 +172,7 @@ pub fn generate(union_args: &args::Union) -> GeneratorResult<TokenStream> {
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
registry.create_output_type::<Self, _>(|registry| {
registry.create_output_type::<Self, _>(#crate_name::registry::MetaTypeId::Union, |registry| {
#(#registry_types)*
#crate_name::registry::MetaType::Union {

@ -1 +1 @@
Subproject commit 6a63808523f7e4995f9111a3497f06921cd3b11b
Subproject commit 61384c8fa50e1395e50015b2be77b4bc6f5de4d2

View File

@ -3,6 +3,7 @@ mod export_sdl;
mod stringify_exec_doc;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::fmt::{self, Display, Formatter};
use indexmap::map::IndexMap;
use indexmap::set::IndexSet;
@ -179,6 +180,29 @@ pub struct MetaEnumValue {
type MetaVisibleFn = fn(&Context<'_>) -> bool;
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum MetaTypeId {
Scalar,
Object,
Interface,
Union,
Enum,
InputObject,
}
impl Display for MetaTypeId {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self {
MetaTypeId::Scalar => "Scalar",
MetaTypeId::Object => "Object",
MetaTypeId::Interface => "Interface",
MetaTypeId::Union => "Union",
MetaTypeId::Enum => "Enum",
MetaTypeId::InputObject => "InputObject",
})
}
}
#[derive(Clone)]
pub enum MetaType {
Scalar {
@ -234,6 +258,18 @@ pub enum MetaType {
}
impl MetaType {
#[inline]
pub fn type_id(&self) -> MetaTypeId {
match self {
MetaType::Scalar { .. } => MetaTypeId::Scalar,
MetaType::Object { .. } => MetaTypeId::Object,
MetaType::Interface { .. } => MetaTypeId::Interface,
MetaType::Union { .. } => MetaTypeId::Union,
MetaType::Enum { .. } => MetaTypeId::Enum,
MetaType::InputObject { .. } => MetaTypeId::InputObject,
}
}
#[inline]
pub fn field_by_name(&self, name: &str) -> Option<&MetaField> {
self.fields().and_then(|fields| fields.get(name))
@ -371,30 +407,45 @@ pub struct Registry {
}
impl Registry {
pub fn create_input_type<T: InputType + ?Sized, F: FnMut(&mut Registry) -> MetaType>(
&mut self,
mut f: F,
) -> String {
self.create_type(&mut f, &*T::type_name(), std::any::type_name::<T>());
pub fn create_input_type<T, F>(&mut self, type_id: MetaTypeId, mut f: F) -> String
where
T: InputType + ?Sized,
F: FnMut(&mut Registry) -> MetaType,
{
self.create_type(
&mut f,
&*T::type_name(),
std::any::type_name::<T>(),
type_id,
);
T::qualified_type_name()
}
pub fn create_output_type<T: OutputType + ?Sized, F: FnMut(&mut Registry) -> MetaType>(
&mut self,
mut f: F,
) -> String {
self.create_type(&mut f, &*T::type_name(), std::any::type_name::<T>());
pub fn create_output_type<T, F>(&mut self, type_id: MetaTypeId, mut f: F) -> String
where
T: OutputType + ?Sized,
F: FnMut(&mut Registry) -> MetaType,
{
self.create_type(
&mut f,
&*T::type_name(),
std::any::type_name::<T>(),
type_id,
);
T::qualified_type_name()
}
pub fn create_subscription_type<
pub fn create_subscription_type<T, F>(&mut self, mut f: F) -> String
where
T: SubscriptionType + ?Sized,
F: FnMut(&mut Registry) -> MetaType,
>(
&mut self,
mut f: F,
) -> String {
self.create_type(&mut f, &*T::type_name(), std::any::type_name::<T>());
{
self.create_type(
&mut f,
&*T::type_name(),
std::any::type_name::<T>(),
MetaTypeId::Object,
);
T::qualified_type_name()
}
@ -403,16 +454,30 @@ impl Registry {
f: &mut F,
name: &str,
rust_typename: &str,
type_id: MetaTypeId,
) {
match self.types.get(name) {
Some(ty) => {
if let Some(prev_typename) = ty.rust_typename() {
if prev_typename != "__fake_type__" && rust_typename != prev_typename {
if prev_typename == "__fake_type__" {
return;
}
if rust_typename != prev_typename {
panic!(
"`{}` and `{}` have the same GraphQL name `{}`",
prev_typename, rust_typename, name,
);
}
if ty.type_id() != type_id {
panic!(
"Register `{}` as `{}`, but it is already registered as `{}`",
name,
type_id,
ty.type_id()
);
}
}
}
None => {

View File

@ -154,12 +154,14 @@ macro_rules! scalar_internal {
fn create_type_info(
registry: &mut $crate::registry::Registry,
) -> ::std::string::String {
registry.create_input_type::<$ty, _>(|_| $crate::registry::MetaType::Scalar {
name: ::std::borrow::ToOwned::to_owned($name),
description: $desc,
is_valid: |value| <$ty as $crate::ScalarType>::is_valid(value),
visible: ::std::option::Option::None,
specified_by_url: $specified_by_url,
registry.create_input_type::<$ty, _>($crate::registry::MetaTypeId::Scalar, |_| {
$crate::registry::MetaType::Scalar {
name: ::std::borrow::ToOwned::to_owned($name),
description: $desc,
is_valid: |value| <$ty as $crate::ScalarType>::is_valid(value),
visible: ::std::option::Option::None,
specified_by_url: $specified_by_url,
}
})
}
@ -187,12 +189,14 @@ macro_rules! scalar_internal {
fn create_type_info(
registry: &mut $crate::registry::Registry,
) -> ::std::string::String {
registry.create_output_type::<$ty, _>(|_| $crate::registry::MetaType::Scalar {
name: ::std::borrow::ToOwned::to_owned($name),
description: $desc,
is_valid: |value| <$ty as $crate::ScalarType>::is_valid(value),
visible: ::std::option::Option::None,
specified_by_url: $specified_by_url,
registry.create_output_type::<$ty, _>($crate::registry::MetaTypeId::Scalar, |_| {
$crate::registry::MetaType::Scalar {
name: ::std::borrow::ToOwned::to_owned($name),
description: $desc,
is_valid: |value| <$ty as $crate::ScalarType>::is_valid(value),
visible: ::std::option::Option::None,
specified_by_url: $specified_by_url,
}
})
}

View File

@ -6,6 +6,7 @@ use indexmap::map::IndexMap;
use crate::connection::edge::Edge;
use crate::connection::page_info::PageInfo;
use crate::parser::types::Field;
use crate::registry::MetaTypeId;
use crate::resolver_utils::{resolve_container, ContainerType};
use crate::types::connection::{CursorType, EmptyFields};
use crate::{
@ -163,7 +164,7 @@ where
}
fn create_type_info(registry: &mut registry::Registry) -> String {
registry.create_output_type::<Self, _>(|registry| {
registry.create_output_type::<Self, _>(MetaTypeId::Object,|registry| {
EC::create_type_info(registry);
let additional_fields = if let Some(registry::MetaType::Object { fields, .. }) =
registry.types.remove(EC::type_name().as_ref())

View File

@ -4,6 +4,7 @@ use indexmap::map::IndexMap;
use crate::connection::EmptyFields;
use crate::parser::types::Field;
use crate::registry::MetaTypeId;
use crate::resolver_utils::{resolve_container, ContainerType};
use crate::types::connection::CursorType;
use crate::{
@ -72,7 +73,7 @@ where
}
fn create_type_info(registry: &mut registry::Registry) -> String {
registry.create_output_type::<Self, _>(|registry| {
registry.create_output_type::<Self, _>(MetaTypeId::Object, |registry| {
let additional_fields = if let registry::MetaType::Object { fields, .. } =
registry.create_fake_output_type::<E>()
{

View File

@ -1,6 +1,7 @@
use std::borrow::Cow;
use crate::parser::types::Field;
use crate::registry::MetaTypeId;
use crate::resolver_utils::ContainerType;
use crate::{
registry, Context, ContextSelectionSet, ObjectType, OutputType, Positioned, ServerError,
@ -49,7 +50,7 @@ impl OutputType for EmptyMutation {
}
fn create_type_info(registry: &mut registry::Registry) -> String {
registry.create_output_type::<Self, _>(|_| registry::MetaType::Object {
registry.create_output_type::<Self, _>(MetaTypeId::Object, |_| registry::MetaType::Object {
name: "EmptyMutation".to_string(),
description: None,
fields: Default::default(),

View File

@ -10,7 +10,7 @@ use indexmap::IndexMap;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::registry::{MetaType, Registry};
use crate::registry::{MetaType, MetaTypeId, Registry};
use crate::{
ContextSelectionSet, InputType, InputValueError, InputValueResult, Name, OutputType,
ServerResult, Value,
@ -29,7 +29,7 @@ where
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_input_type::<Self, _>(|_| MetaType::Scalar {
registry.create_input_type::<Self, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
name: <Self as InputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON Object value."),
is_valid: |_| true,
@ -84,7 +84,7 @@ where
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_output_type::<Self, _>(|_| MetaType::Scalar {
registry.create_output_type::<Self, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
name: <Self as OutputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON Object value."),
is_valid: |_| true,

View File

@ -11,7 +11,7 @@ use indexmap::IndexMap;
use serde::de::DeserializeOwned;
use serde::Serialize;
use crate::registry::{MetaType, Registry};
use crate::registry::{MetaType, MetaTypeId, Registry};
use crate::{
ContextSelectionSet, InputType, InputValueError, InputValueResult, Name, OutputType,
ServerResult, Value,
@ -31,7 +31,7 @@ where
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_input_type::<Self, _>(|_| MetaType::Scalar {
registry.create_input_type::<Self, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
name: <Self as InputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON Object value."),
is_valid: |_| true,
@ -87,7 +87,7 @@ where
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_output_type::<Self, _>(|_| MetaType::Scalar {
registry.create_output_type::<Self, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
name: <Self as OutputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON Object value."),
is_valid: |_| true,

View File

@ -5,7 +5,7 @@ use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use crate::parser::types::Field;
use crate::registry::{MetaType, Registry};
use crate::registry::{MetaType, MetaTypeId, Registry};
use crate::{
from_value, to_value, ContextSelectionSet, InputType, InputValueResult, OutputType, Positioned,
ServerResult, Value,
@ -46,7 +46,7 @@ impl<T: DeserializeOwned + Serialize + Send + Sync> InputType for Json<T> {
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_input_type::<Json<T>, _>(|_| MetaType::Scalar {
registry.create_input_type::<Json<T>, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
name: <Self as InputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON value."),
is_valid: |_| true,
@ -75,7 +75,7 @@ impl<T: Serialize + Send + Sync> OutputType for Json<T> {
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_output_type::<Json<T>, _>(|_| MetaType::Scalar {
registry.create_output_type::<Json<T>, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
name: <Self as OutputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON value."),
is_valid: |_| true,
@ -101,12 +101,14 @@ impl InputType for serde_json::Value {
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_input_type::<serde_json::Value, _>(|_| MetaType::Scalar {
name: <Self as InputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON value."),
is_valid: |_| true,
visible: None,
specified_by_url: None,
registry.create_input_type::<serde_json::Value, _>(MetaTypeId::Scalar, |_| {
MetaType::Scalar {
name: <Self as InputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON value."),
is_valid: |_| true,
visible: None,
specified_by_url: None,
}
})
}
@ -130,12 +132,14 @@ impl OutputType for serde_json::Value {
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_output_type::<serde_json::Value, _>(|_| MetaType::Scalar {
name: <Self as OutputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON value."),
is_valid: |_| true,
visible: None,
specified_by_url: None,
registry.create_output_type::<serde_json::Value, _>(MetaTypeId::Scalar, |_| {
MetaType::Scalar {
name: <Self as OutputType>::type_name().to_string(),
description: Some("A scalar that can represent any JSON value."),
is_valid: |_| true,
visible: None,
specified_by_url: None,
}
})
}

View File

@ -5,7 +5,7 @@ use indexmap::IndexMap;
use crate::futures_util::Stream;
use crate::parser::types::Field;
use crate::registry::{MetaType, Registry};
use crate::registry::{MetaType, MetaTypeId, Registry};
use crate::{
CacheControl, ContainerType, Context, ContextSelectionSet, OutputType, Positioned, Response,
ServerResult, SimpleObject, SubscriptionType, Value,
@ -48,7 +48,7 @@ where
}
fn create_type_info(registry: &mut Registry) -> String {
registry.create_output_type::<Self, _>(|registry| {
registry.create_output_type::<Self, _>(MetaTypeId::Object, |registry| {
let mut fields = IndexMap::new();
let mut cc = CacheControl::default();

View File

@ -5,6 +5,7 @@ use std::io::Read;
#[cfg(feature = "unblock")]
use futures_util::io::AsyncRead;
use crate::registry::MetaTypeId;
use crate::{registry, Context, InputType, InputValueError, InputValueResult, Value};
/// A file upload value.
@ -109,7 +110,7 @@ impl InputType for Upload {
}
fn create_type_info(registry: &mut registry::Registry) -> String {
registry.create_input_type::<Self, _>(|_| registry::MetaType::Scalar {
registry.create_input_type::<Self, _>(MetaTypeId::Scalar, |_| registry::MetaType::Scalar {
name: Self::type_name().to_string(),
description: None,
is_valid: |value| matches!(value, Value::String(_)),

View File

@ -407,6 +407,31 @@ pub async fn test_both_input_output() {
assert_eq!(<MyObject as OutputType>::type_name(), "MyObject");
}
#[test]
#[should_panic]
pub fn test_both_input_output_with_same_name() {
#[derive(SimpleObject, InputObject)]
#[allow(dead_code)]
struct MyObject {
#[graphql(default = 10)]
a: i32,
b: bool,
#[graphql(skip)]
c: String,
}
struct Query;
#[Object]
impl Query {
async fn obj(&self, input: MyObject) -> MyObject {
input
}
}
Schema::new(Query, EmptyMutation, EmptySubscription);
}
#[tokio::test]
pub async fn test_skip_input() {
#[derive(SimpleObject, InputObject)]