Skip to content

Commit d7ea1c9

Browse files
committed
Implement #[derive(GraphQLUnion)] for enums
- add marker::GraphQLUnion trait in 'juniper' Additionally: - re-export 'futures' crate in 'juniper' for convenient reuse in generated code without requiring library user to provide 'futures' crate by himself
1 parent 0a6a943 commit d7ea1c9

18 files changed

+751
-63
lines changed

juniper/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@ extern crate uuid;
111111
#[cfg(any(test, feature = "bson"))]
112112
extern crate bson;
113113

114+
// This one is required for use by code generated with`juniper_codegen` macros.
115+
#[doc(hidden)]
116+
pub use futures;
117+
114118
// Depend on juniper_codegen and re-export everything in it.
115119
// This allows users to just depend on juniper and get the derive
116120
// functionality automatically.
@@ -179,7 +183,7 @@ pub use crate::{
179183
types::{
180184
async_await::GraphQLTypeAsync,
181185
base::{Arguments, GraphQLType, TypeKind},
182-
marker,
186+
marker::{self, GraphQLUnion},
183187
scalars::{EmptyMutation, EmptySubscription, ID},
184188
subscriptions::{GraphQLSubscriptionType, SubscriptionConnection, SubscriptionCoordinator},
185189
},

juniper/src/types/marker.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,30 @@ pub trait GraphQLObjectType<S: ScalarValue>: GraphQLType<S> {
2323
fn mark() {}
2424
}
2525

26+
/// Maker trait for [GraphQL unions][1].
27+
///
28+
/// This trait extends the [`GraphQLType`] and is only used to mark [union][1]. During compile this
29+
/// addition information is required to prevent unwanted structure compiling. If an object requires
30+
/// this trait instead of the [`GraphQLType`], then it explicitly requires [GraphQL unions][1].
31+
/// Other types ([scalars][2], [enums][3], [objects][4], [input objects][5] and [interfaces][6]) are
32+
/// not allowed.
33+
///
34+
/// [1]: https://spec.graphql.org/June2018/#sec-Unions
35+
/// [2]: https://spec.graphql.org/June2018/#sec-Scalars
36+
/// [3]: https://spec.graphql.org/June2018/#sec-Enums
37+
/// [4]: https://spec.graphql.org/June2018/#sec-Objects
38+
/// [5]: https://spec.graphql.org/June2018/#sec-Input-Objects
39+
/// [6]: https://spec.graphql.org/June2018/#sec-Interfaces
40+
pub trait GraphQLUnion: GraphQLType {
41+
/// An arbitrary function without meaning.
42+
///
43+
/// May contain compile timed check logic which ensures that types are used correctly according
44+
/// to the [GraphQL specification][1].
45+
///
46+
/// [1]: https://spec.graphql.org/June2018/
47+
fn mark() {}
48+
}
49+
2650
/// Marker trait for types which can be used as output types.
2751
///
2852
/// The GraphQL specification differentiates between input and output

juniper_codegen/src/derive_enum.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ pub fn impl_enum(
145145
include_type_generics: true,
146146
generic_scalar: true,
147147
no_async: attrs.no_async.is_some(),
148+
mode: is_internal.into(),
148149
};
149150

150151
let juniper_crate_name = if is_internal { "crate" } else { "juniper" };

juniper_codegen/src/derive_input_object.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ pub fn impl_input_object(
145145
include_type_generics: true,
146146
generic_scalar: true,
147147
no_async: attrs.no_async.is_some(),
148+
mode: is_internal.into(),
148149
};
149150

150151
let juniper_crate_name = if is_internal { "crate" } else { "juniper" };

juniper_codegen/src/derive_object.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ pub fn build_derive_object(
132132
include_type_generics: true,
133133
generic_scalar: true,
134134
no_async: attrs.no_async.is_some(),
135+
mode: is_internal.into(),
135136
};
136137

137138
let juniper_crate_name = if is_internal { "crate" } else { "juniper" };

juniper_codegen/src/derive_scalar_value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ fn impl_scalar_struct(
127127
executor: &'a #crate_name::Executor<Self::Context, __S>,
128128
) -> #crate_name::BoxFuture<'a, #crate_name::ExecutionResult<__S>> {
129129
use #crate_name::GraphQLType;
130-
use futures::future;
130+
use #crate_name::futures::future;
131131
let v = self.resolve(info, selection_set, executor);
132132
Box::pin(future::ready(v))
133133
}

juniper_codegen/src/derive_union.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use proc_macro2::TokenStream;
2+
use proc_macro_error::ResultExt as _;
23
use quote::quote;
34
use syn::{self, ext::IdentExt, spanned::Spanned, Data, Fields};
45

@@ -10,12 +11,9 @@ use crate::{
1011
const SCOPE: GraphQLScope = GraphQLScope::DeriveUnion;
1112

1213
pub fn expand(input: TokenStream, mode: Mode) -> syn::Result<TokenStream> {
13-
let is_internal = matches!(mode, Mode::Internal);
14-
15-
let ast =
16-
syn::parse2::<syn::DeriveInput>(input).unwrap_or_else(|e| proc_macro_error::abort!(e));
17-
14+
let ast = syn::parse2::<syn::DeriveInput>(input).unwrap_or_abort();
1815
let ast_span = ast.span();
16+
1917
let enum_fields = match ast.data {
2018
Data::Enum(data) => data.variants,
2119
Data::Struct(_) => unimplemented!(),
@@ -66,10 +64,7 @@ pub fn expand(input: TokenStream, mode: Mode) -> syn::Result<TokenStream> {
6664
let _type = match field.fields {
6765
Fields::Unnamed(inner) => {
6866
let mut iter = inner.unnamed.iter();
69-
let first = match iter.next() {
70-
Some(val) => val,
71-
None => unreachable!(),
72-
};
67+
let first = iter.next().unwrap();
7368

7469
if iter.next().is_some() {
7570
SCOPE.custom(
@@ -140,7 +135,7 @@ pub fn expand(input: TokenStream, mode: Mode) -> syn::Result<TokenStream> {
140135
SCOPE.not_empty(ast_span);
141136
}
142137

143-
if name.starts_with("__") && !is_internal {
138+
if name.starts_with("__") && matches!(mode, Mode::Public) {
144139
SCOPE.no_double_underscore(if let Some(name) = attrs.name {
145140
name.span_ident()
146141
} else {
@@ -180,8 +175,8 @@ pub fn expand(input: TokenStream, mode: Mode) -> syn::Result<TokenStream> {
180175
include_type_generics: true,
181176
generic_scalar: true,
182177
no_async: attrs.no_async.is_some(),
178+
mode,
183179
};
184180

185-
let juniper_crate_name = if is_internal { "crate" } else { "juniper" };
186-
Ok(definition.into_union_tokens(juniper_crate_name))
181+
Ok(definition.into_union_tokens())
187182
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)