Skip to content

Commit 0a6a943

Browse files
committed
Refactor ground for union macros reimplementation
1 parent 91a3353 commit 0a6a943

File tree

7 files changed

+137
-107
lines changed

7 files changed

+137
-107
lines changed

juniper_codegen/Cargo.toml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[package]
22
name = "juniper_codegen"
33
version = "0.14.2"
4+
edition = "2018"
45
authors = [
56
"Magnus Hallin <[email protected]>",
67
"Christoph Herzog <[email protected]>",
@@ -9,20 +10,19 @@ description = "Internal custom derive trait for Juniper GraphQL"
910
license = "BSD-2-Clause"
1011
documentation = "https://docs.rs/juniper"
1112
repository = "https://github.com/graphql-rust/juniper"
12-
edition = "2018"
13+
14+
[badges]
15+
travis-ci = { repository = "graphql-rust/juniper" }
1316

1417
[lib]
1518
proc-macro = true
1619

1720
[dependencies]
21+
proc-macro-error = "1.0.2"
1822
proc-macro2 = "1.0.1"
19-
syn = { version = "1.0.3", features = ["full", "extra-traits", "parsing"] }
2023
quote = "1.0.3"
21-
futures = "0.3.1"
22-
proc-macro-error = "1.0.2"
24+
syn = { version = "1.0.3", features = ["full", "extra-traits", "parsing"] }
2325

2426
[dev-dependencies]
25-
juniper = { version = "0.14.2", path = "../juniper"}
26-
27-
[badges]
28-
travis-ci = { repository = "graphql-rust/juniper" }
27+
futures = "0.3.1"
28+
juniper = { version = "0.14.2", path = "../juniper" }

juniper_codegen/src/derive_union.rs

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
1-
use crate::{
2-
result::{GraphQLScope, UnsupportedAttribute},
3-
util::{self, span_container::SpanContainer},
4-
};
51
use proc_macro2::TokenStream;
62
use quote::quote;
73
use syn::{self, ext::IdentExt, spanned::Spanned, Data, Fields};
84

9-
pub fn build_derive_union(
10-
ast: syn::DeriveInput,
11-
is_internal: bool,
12-
error: GraphQLScope,
13-
) -> syn::Result<TokenStream> {
5+
use crate::{
6+
result::{GraphQLScope, UnsupportedAttribute},
7+
util::{self, span_container::SpanContainer, Mode},
8+
};
9+
10+
const SCOPE: GraphQLScope = GraphQLScope::DeriveUnion;
11+
12+
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+
1418
let ast_span = ast.span();
1519
let enum_fields = match ast.data {
1620
Data::Enum(data) => data.variants,
17-
_ => return Err(error.custom_error(ast_span, "can only be applied to enums")),
21+
Data::Struct(_) => unimplemented!(),
22+
_ => return Err(SCOPE.custom_error(ast_span, "can only be applied to enums and structs")),
1823
};
1924

2025
// Parse attributes.
@@ -43,7 +48,7 @@ pub fn build_derive_union(
4348
};
4449

4550
if let Some(ident) = field_attrs.skip {
46-
error.unsupported_attribute_within(ident.span(), UnsupportedAttribute::Skip);
51+
SCOPE.unsupported_attribute_within(ident.span(), UnsupportedAttribute::Skip);
4752
return None;
4853
}
4954

@@ -67,7 +72,7 @@ pub fn build_derive_union(
6772
};
6873

6974
if iter.next().is_some() {
70-
error.custom(
75+
SCOPE.custom(
7176
inner.span(),
7277
"all members must be unnamed with a single element e.g. Some(T)",
7378
);
@@ -76,7 +81,7 @@ pub fn build_derive_union(
7681
first.ty.clone()
7782
}
7883
_ => {
79-
error.custom(
84+
SCOPE.custom(
8085
variant_name.span(),
8186
"only unnamed fields with a single element are allowed, e.g., Some(T)",
8287
);
@@ -86,21 +91,21 @@ pub fn build_derive_union(
8691
};
8792

8893
if let Some(description) = field_attrs.description {
89-
error.unsupported_attribute_within(
94+
SCOPE.unsupported_attribute_within(
9095
description.span_ident(),
9196
UnsupportedAttribute::Description,
9297
);
9398
}
9499

95100
if let Some(default) = field_attrs.default {
96-
error.unsupported_attribute_within(
101+
SCOPE.unsupported_attribute_within(
97102
default.span_ident(),
98103
UnsupportedAttribute::Default,
99104
);
100105
}
101106

102107
if name.starts_with("__") {
103-
error.no_double_underscore(if let Some(name) = field_attrs.name {
108+
SCOPE.no_double_underscore(if let Some(name) = field_attrs.name {
104109
name.span_ident()
105110
} else {
106111
variant_name.span()
@@ -127,16 +132,16 @@ pub fn build_derive_union(
127132

128133
if !attrs.interfaces.is_empty() {
129134
attrs.interfaces.iter().for_each(|elm| {
130-
error.unsupported_attribute(elm.span(), UnsupportedAttribute::Interface)
135+
SCOPE.unsupported_attribute(elm.span(), UnsupportedAttribute::Interface)
131136
});
132137
}
133138

134139
if fields.is_empty() {
135-
error.not_empty(ast_span);
140+
SCOPE.not_empty(ast_span);
136141
}
137142

138143
if name.starts_with("__") && !is_internal {
139-
error.no_double_underscore(if let Some(name) = attrs.name {
144+
SCOPE.no_double_underscore(if let Some(name) = attrs.name {
140145
name.span_ident()
141146
} else {
142147
ident.span()
@@ -157,7 +162,7 @@ pub fn build_derive_union(
157162
};
158163

159164
if !all_variants_different {
160-
error.custom(ident.span(), "each variant must have a different type");
165+
SCOPE.custom(ident.span(), "each variant must have a different type");
161166
}
162167

163168
// Early abort after GraphQL properties

juniper_codegen/src/impl_union.rs

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,25 @@
1-
use crate::{
2-
result::GraphQLScope,
3-
util::{self, span_container::SpanContainer},
4-
};
51
use proc_macro2::TokenStream;
62
use quote::quote;
73
use syn::{ext::IdentExt, spanned::Spanned};
84

9-
struct ResolverVariant {
10-
pub ty: syn::Type,
11-
pub resolver: syn::Expr,
12-
}
13-
14-
struct ResolveBody {
15-
pub variants: Vec<ResolverVariant>,
16-
}
17-
18-
impl syn::parse::Parse for ResolveBody {
19-
fn parse(input: syn::parse::ParseStream) -> Result<Self, syn::parse::Error> {
20-
input.parse::<syn::token::Match>()?;
21-
input.parse::<syn::token::SelfValue>()?;
22-
23-
let match_body;
24-
syn::braced!( match_body in input );
25-
26-
let mut variants = Vec::new();
27-
while !match_body.is_empty() {
28-
let ty = match_body.parse::<syn::Type>()?;
29-
match_body.parse::<syn::token::FatArrow>()?;
30-
let resolver = match_body.parse::<syn::Expr>()?;
5+
use crate::{
6+
result::GraphQLScope,
7+
util::{self, span_container::SpanContainer, Mode},
8+
};
319

32-
variants.push(ResolverVariant { ty, resolver });
10+
const SCOPE: GraphQLScope = GraphQLScope::ImplUnion;
3311

34-
// Optinal trailing comma.
35-
match_body.parse::<syn::token::Comma>().ok();
36-
}
12+
pub fn expand(attrs: TokenStream, body: TokenStream, mode: Mode) -> syn::Result<TokenStream> {
13+
let is_internal = matches!(mode, Mode::Internal);
3714

38-
if !input.is_empty() {
39-
return Err(input.error("unexpected input"));
40-
}
41-
42-
Ok(Self { variants })
43-
}
44-
}
45-
46-
pub fn impl_union(
47-
is_internal: bool,
48-
attrs: TokenStream,
49-
body: TokenStream,
50-
error: GraphQLScope,
51-
) -> syn::Result<TokenStream> {
5215
let body_span = body.span();
5316
let _impl = util::parse_impl::ImplBlock::parse(attrs, body)?;
5417

5518
// FIXME: what is the purpose of this construct?
5619
// Validate trait target name, if present.
5720
if let Some((name, path)) = &_impl.target_trait {
5821
if !(name == "GraphQLUnion" || name == "juniper.GraphQLUnion") {
59-
return Err(error.custom_error(
22+
return Err(SCOPE.custom_error(
6023
path.span(),
6124
"Invalid impl target trait: expected 'GraphQLUnion'",
6225
));
@@ -89,7 +52,7 @@ pub fn impl_union(
8952
let method = match method {
9053
Some(method) => method,
9154
None => {
92-
return Err(error.custom_error(
55+
return Err(SCOPE.custom_error(
9356
body_span,
9457
"expected exactly one method with signature: fn resolve(&self) { ... }",
9558
))
@@ -103,7 +66,7 @@ pub fn impl_union(
10366
let body = syn::parse::<ResolveBody>(body_raw.into())?;
10467

10568
if body.variants.is_empty() {
106-
error.not_empty(method.span())
69+
SCOPE.not_empty(method.span())
10770
}
10871

10972
proc_macro_error::abort_if_dirty();
@@ -221,3 +184,40 @@ pub fn impl_union(
221184

222185
Ok(output.into())
223186
}
187+
188+
struct ResolverVariant {
189+
pub ty: syn::Type,
190+
pub resolver: syn::Expr,
191+
}
192+
193+
struct ResolveBody {
194+
pub variants: Vec<ResolverVariant>,
195+
}
196+
197+
impl syn::parse::Parse for ResolveBody {
198+
fn parse(input: syn::parse::ParseStream) -> Result<Self, syn::parse::Error> {
199+
input.parse::<syn::token::Match>()?;
200+
input.parse::<syn::token::SelfValue>()?;
201+
202+
let match_body;
203+
syn::braced!( match_body in input );
204+
205+
let mut variants = Vec::new();
206+
while !match_body.is_empty() {
207+
let ty = match_body.parse::<syn::Type>()?;
208+
match_body.parse::<syn::token::FatArrow>()?;
209+
let resolver = match_body.parse::<syn::Expr>()?;
210+
211+
variants.push(ResolverVariant { ty, resolver });
212+
213+
// Optinal trailing comma.
214+
match_body.parse::<syn::token::Comma>().ok();
215+
}
216+
217+
if !input.is_empty() {
218+
return Err(input.error("unexpected input"));
219+
}
220+
221+
Ok(Self { variants })
222+
}
223+
}

juniper_codegen/src/lib.rs

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ use proc_macro::TokenStream;
2525
use proc_macro_error::proc_macro_error;
2626
use result::GraphQLScope;
2727

28+
use self::util::Mode;
29+
2830
#[proc_macro_error]
2931
#[proc_macro_derive(GraphQLEnum, attributes(graphql))]
3032
pub fn derive_enum(input: TokenStream) -> TokenStream {
@@ -93,16 +95,6 @@ pub fn derive_object_internal(input: TokenStream) -> TokenStream {
9395
}
9496
}
9597

96-
#[proc_macro_error]
97-
#[proc_macro_derive(GraphQLUnion, attributes(graphql))]
98-
pub fn derive_union(input: TokenStream) -> TokenStream {
99-
let ast = syn::parse::<syn::DeriveInput>(input).unwrap();
100-
let gen = derive_union::build_derive_union(ast, false, GraphQLScope::DeriveUnion);
101-
match gen {
102-
Ok(gen) => gen.into(),
103-
Err(err) => proc_macro_error::abort!(err),
104-
}
105-
}
10698
/// This custom derive macro implements the #[derive(GraphQLScalarValue)]
10799
/// derive.
108100
///
@@ -554,27 +546,36 @@ pub fn graphql_subscription_internal(args: TokenStream, input: TokenStream) -> T
554546
))
555547
}
556548

549+
#[proc_macro_error]
550+
#[proc_macro_derive(GraphQLUnion, attributes(graphql))]
551+
pub fn derive_union(input: TokenStream) -> TokenStream {
552+
derive_union::expand(input.into(), Mode::Public)
553+
.unwrap_or_else(|e| proc_macro_error::abort!(e))
554+
.into()
555+
}
556+
557+
#[proc_macro_error]
558+
#[proc_macro_derive(GraphQLUnionInternal, attributes(graphql))]
559+
#[doc(hidden)]
560+
pub fn derive_union_internal(input: TokenStream) -> TokenStream {
561+
derive_union::expand(input.into(), Mode::Internal)
562+
.unwrap_or_else(|e| proc_macro_error::abort!(e))
563+
.into()
564+
}
565+
557566
#[proc_macro_error]
558567
#[proc_macro_attribute]
559568
pub fn graphql_union(attrs: TokenStream, body: TokenStream) -> TokenStream {
560-
let attrs = proc_macro2::TokenStream::from(attrs);
561-
let body = proc_macro2::TokenStream::from(body);
562-
let gen = impl_union::impl_union(false, attrs, body, GraphQLScope::ImplUnion);
563-
match gen {
564-
Ok(gen) => gen.into(),
565-
Err(err) => proc_macro_error::abort!(err),
566-
}
569+
impl_union::expand(attrs.into(), body.into(), Mode::Public)
570+
.unwrap_or_else(|e| proc_macro_error::abort!(e))
571+
.into()
567572
}
568573

569574
#[proc_macro_error]
570575
#[proc_macro_attribute]
571576
#[doc(hidden)]
572577
pub fn graphql_union_internal(attrs: TokenStream, body: TokenStream) -> TokenStream {
573-
let attrs = proc_macro2::TokenStream::from(attrs);
574-
let body = proc_macro2::TokenStream::from(body);
575-
let gen = impl_union::impl_union(true, attrs, body, GraphQLScope::ImplUnion);
576-
match gen {
577-
Ok(gen) => gen.into(),
578-
Err(err) => proc_macro_error::abort!(err),
579-
}
578+
impl_union::expand(attrs.into(), body.into(), Mode::Internal)
579+
.unwrap_or_else(|e| proc_macro_error::abort!(e))
580+
.into()
580581
}

0 commit comments

Comments
 (0)