Skip to content

Commit 9420f3c

Browse files
authored
Allow top-level #[graphql] attribute in attribute macros (#1232)
1 parent c0e1b3e commit 9420f3c

File tree

12 files changed

+140
-76
lines changed

12 files changed

+140
-76
lines changed

juniper_codegen/src/common/mod.rs

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,51 @@ pub(crate) mod rename;
1111
pub(crate) mod scalar;
1212
mod span_container;
1313

14+
use std::slice;
15+
1416
pub(crate) use self::{description::Description, span_container::SpanContainer};
1517

16-
/// Checks whether the specified [`syn::Path`] equals to one-segment string
17-
/// `value`.
18-
pub(crate) fn path_eq_single(path: &syn::Path, value: &str) -> bool {
19-
path.segments.len() == 1 && path.segments[0].ident == value
18+
/// Checks whether the specified [`syn::Path`] equals to one of specified one-segment
19+
/// [`AttrNames::values`].
20+
pub(crate) fn path_eq_single(path: &syn::Path, names: impl AttrNames) -> bool {
21+
path.segments.len() == 1
22+
&& names
23+
.values()
24+
.iter()
25+
.any(|name| path.segments[0].ident == name)
2026
}
2127

2228
/// Filters the provided [`syn::Attribute`] to contain only ones with the
2329
/// specified `name`.
2430
pub(crate) fn filter_attrs<'a>(
25-
name: &'a str,
31+
names: impl AttrNames + 'a,
2632
attrs: &'a [syn::Attribute],
2733
) -> impl Iterator<Item = &'a syn::Attribute> + 'a {
2834
attrs
2935
.iter()
30-
.filter(move |attr| path_eq_single(attr.path(), name))
36+
.filter(move |attr| path_eq_single(attr.path(), names))
37+
}
38+
39+
/// Input-type polymorphism helper for checking names of multiple attribute names.
40+
pub(crate) trait AttrNames: Copy {
41+
/// Returns values to be checked.
42+
fn values(&self) -> &[&str];
43+
}
44+
45+
impl AttrNames for &str {
46+
fn values(&self) -> &[&str] {
47+
slice::from_ref(self)
48+
}
49+
}
50+
51+
impl AttrNames for &[&str] {
52+
fn values(&self) -> &[&str] {
53+
self
54+
}
55+
}
56+
57+
impl<const N: usize> AttrNames for [&str; N] {
58+
fn values(&self) -> &[&str] {
59+
self
60+
}
3161
}

juniper_codegen/src/common/parse/attr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use proc_macro2::{Span, TokenStream};
55
use syn::parse_quote;
66

7-
use crate::common::path_eq_single;
7+
use crate::common::{path_eq_single, AttrNames};
88

99
/// Prepends the given `attrs` collection with a new [`syn::Attribute`] generated from the given
1010
/// `attr_path` and `attr_args`.
@@ -25,10 +25,10 @@ pub(crate) fn unite(
2525
///
2626
/// This function is generally used for removing duplicate attributes during `proc_macro_attribute`
2727
/// expansion, so avoid unnecessary expansion duplication.
28-
pub(crate) fn strip(attr_path: &str, attrs: Vec<syn::Attribute>) -> Vec<syn::Attribute> {
28+
pub(crate) fn strip(names: impl AttrNames, attrs: Vec<syn::Attribute>) -> Vec<syn::Attribute> {
2929
attrs
3030
.into_iter()
31-
.filter(|attr| !path_eq_single(attr.path(), attr_path))
31+
.filter(|attr| !path_eq_single(attr.path(), names))
3232
.collect()
3333
}
3434

juniper_codegen/src/graphql_interface/attr.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ const ERR: diagnostic::Scope = diagnostic::Scope::InterfaceAttr;
2121
pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
2222
if let Ok(mut ast) = syn::parse2::<syn::ItemTrait>(body.clone()) {
2323
let trait_attrs = parse::attr::unite(("graphql_interface", &attr_args), &ast.attrs);
24-
ast.attrs = parse::attr::strip("graphql_interface", ast.attrs);
24+
ast.attrs = parse::attr::strip(["graphql_interface", "graphql"], ast.attrs);
2525
return expand_on_trait(trait_attrs, ast);
2626
}
2727
if let Ok(mut ast) = syn::parse2::<syn::DeriveInput>(body) {
2828
let trait_attrs = parse::attr::unite(("graphql_interface", &attr_args), &ast.attrs);
29-
ast.attrs = parse::attr::strip("graphql_interface", ast.attrs);
29+
ast.attrs = parse::attr::strip(["graphql_interface", "graphql"], ast.attrs);
3030
return expand_on_derive_input(trait_attrs, ast);
3131
}
3232

@@ -42,7 +42,7 @@ fn expand_on_trait(
4242
attrs: Vec<syn::Attribute>,
4343
mut ast: syn::ItemTrait,
4444
) -> syn::Result<TokenStream> {
45-
let attr = Attr::from_attrs("graphql_interface", &attrs)?;
45+
let attr = Attr::from_attrs(["graphql_interface", "graphql"], &attrs)?;
4646

4747
let trait_ident = &ast.ident;
4848
let trait_span = ast.span();
@@ -220,7 +220,7 @@ fn expand_on_derive_input(
220220
attrs: Vec<syn::Attribute>,
221221
mut ast: syn::DeriveInput,
222222
) -> syn::Result<TokenStream> {
223-
let attr = Attr::from_attrs("graphql_interface", &attrs)?;
223+
let attr = Attr::from_attrs(["graphql_interface", "graphql"], &attrs)?;
224224

225225
let struct_ident = &ast.ident;
226226
let struct_span = ast.span();

juniper_codegen/src/graphql_interface/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::common::{
2525
attr::{err, OptionExt as _},
2626
GenericsExt as _, ParseBufferExt as _,
2727
},
28-
rename, scalar, Description, SpanContainer,
28+
rename, scalar, AttrNames, Description, SpanContainer,
2929
};
3030

3131
/// Returns [`syn::Ident`]s for a generic enum deriving [`Clone`] and [`Copy`]
@@ -254,10 +254,10 @@ impl Attr {
254254
})
255255
}
256256

257-
/// Parses [`TraitAttr`] from the given multiple `name`d [`syn::Attribute`]s
258-
/// placed on a trait definition.
259-
fn from_attrs(name: &str, attrs: &[syn::Attribute]) -> syn::Result<Self> {
260-
let mut attr = filter_attrs(name, attrs)
257+
/// Parses a [`TraitAttr`] from the provided multiple [`syn::Attribute`]s with
258+
/// the specified `names`, placed on a trait or struct definition.
259+
fn from_attrs(names: impl AttrNames, attrs: &[syn::Attribute]) -> syn::Result<Self> {
260+
let mut attr = filter_attrs(names, attrs)
261261
.map(|attr| attr.parse_args())
262262
.try_fold(Self::default(), |prev, curr| prev.try_merge(curr?))?;
263263

juniper_codegen/src/graphql_object/attr.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStr
2222
if let Ok(mut ast) = syn::parse2::<syn::ItemImpl>(body) {
2323
if ast.trait_.is_none() {
2424
let impl_attrs = parse::attr::unite(("graphql_object", &attr_args), &ast.attrs);
25-
ast.attrs = parse::attr::strip("graphql_object", ast.attrs);
26-
return expand_on_impl::<Query>(Attr::from_attrs("graphql_object", &impl_attrs)?, ast);
25+
ast.attrs = parse::attr::strip(["graphql_object", "graphql"], ast.attrs);
26+
return expand_on_impl::<Query>(
27+
Attr::from_attrs(["graphql_object", "graphql"], &impl_attrs)?,
28+
ast,
29+
);
2730
}
2831
}
2932

juniper_codegen/src/graphql_object/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::common::{
2323
attr::{err, OptionExt as _},
2424
GenericsExt as _, ParseBufferExt as _, TypeExt,
2525
},
26-
rename, scalar, Description, SpanContainer,
26+
rename, scalar, AttrNames, Description, SpanContainer,
2727
};
2828

2929
/// Available arguments behind `#[graphql]` (or `#[graphql_object]`) attribute
@@ -181,10 +181,10 @@ impl Attr {
181181
})
182182
}
183183

184-
/// Parses [`Attr`] from the given multiple `name`d [`syn::Attribute`]s
185-
/// placed on a struct or impl block definition.
186-
pub(crate) fn from_attrs(name: &str, attrs: &[syn::Attribute]) -> syn::Result<Self> {
187-
let mut attr = filter_attrs(name, attrs)
184+
/// Parses an [`Attr`] from the provided multiple [`syn::Attribute`]s with
185+
/// the specified `names`, placed on a struct or impl block definition.
186+
pub(crate) fn from_attrs(names: impl AttrNames, attrs: &[syn::Attribute]) -> syn::Result<Self> {
187+
let mut attr = filter_attrs(names, attrs)
188188
.map(|attr| attr.parse_args())
189189
.try_fold(Self::default(), |prev, curr| prev.try_merge(curr?))?;
190190

juniper_codegen/src/graphql_scalar/attr.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ const ERR: diagnostic::Scope = diagnostic::Scope::ScalarAttr;
1515
pub(crate) fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
1616
if let Ok(mut ast) = syn::parse2::<syn::ItemType>(body.clone()) {
1717
let attrs = parse::attr::unite(("graphql_scalar", &attr_args), &ast.attrs);
18-
ast.attrs = parse::attr::strip("graphql_scalar", ast.attrs);
18+
ast.attrs = parse::attr::strip(["graphql_scalar", "graphql"], ast.attrs);
1919
return expand_on_type_alias(attrs, ast);
2020
} else if let Ok(mut ast) = syn::parse2::<syn::DeriveInput>(body) {
2121
let attrs = parse::attr::unite(("graphql_scalar", &attr_args), &ast.attrs);
22-
ast.attrs = parse::attr::strip("graphql_scalar", ast.attrs);
22+
ast.attrs = parse::attr::strip(["graphql_scalar", "graphql"], ast.attrs);
2323
return expand_on_derive_input(attrs, ast);
2424
}
2525

@@ -35,7 +35,7 @@ fn expand_on_type_alias(
3535
attrs: Vec<syn::Attribute>,
3636
ast: syn::ItemType,
3737
) -> syn::Result<TokenStream> {
38-
let attr = Attr::from_attrs("graphql_scalar", &attrs)?;
38+
let attr = Attr::from_attrs(["graphql_scalar", "graphql"], &attrs)?;
3939
if attr.transparent {
4040
return Err(ERR.custom_error(
4141
ast.span(),
@@ -73,7 +73,7 @@ fn expand_on_derive_input(
7373
attrs: Vec<syn::Attribute>,
7474
ast: syn::DeriveInput,
7575
) -> syn::Result<TokenStream> {
76-
let attr = Attr::from_attrs("graphql_scalar", &attrs)?;
76+
let attr = Attr::from_attrs(["graphql_scalar", "graphql"], &attrs)?;
7777
let methods = parse_derived_methods(&ast, &attr)?;
7878
let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);
7979

juniper_codegen/src/graphql_scalar/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::common::{
2020
attr::{err, OptionExt as _},
2121
ParseBufferExt as _,
2222
},
23-
scalar, Description, SpanContainer,
23+
scalar, AttrNames, Description, SpanContainer,
2424
};
2525

2626
pub mod attr;
@@ -240,10 +240,10 @@ impl Attr {
240240
})
241241
}
242242

243-
/// Parses [`Attr`] from the given multiple `name`d [`syn::Attribute`]s
244-
/// placed on a trait definition.
245-
fn from_attrs(name: &str, attrs: &[syn::Attribute]) -> syn::Result<Self> {
246-
let mut attr = filter_attrs(name, attrs)
243+
/// Parses an [`Attr`] from the provided multiple [`syn::Attribute`]s with
244+
/// the specified `names`, placed on a type definition.
245+
fn from_attrs(names: impl AttrNames, attrs: &[syn::Attribute]) -> syn::Result<Self> {
246+
let mut attr = filter_attrs(names, attrs)
247247
.map(|attr| attr.parse_args())
248248
.try_fold(Self::default(), |prev, curr| prev.try_merge(curr?))?;
249249

juniper_codegen/src/graphql_subscription/attr.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStr
1414
if let Ok(mut ast) = syn::parse2::<syn::ItemImpl>(body) {
1515
if ast.trait_.is_none() {
1616
let impl_attrs = parse::attr::unite(("graphql_subscription", &attr_args), &ast.attrs);
17-
ast.attrs = parse::attr::strip("graphql_subscription", ast.attrs);
17+
ast.attrs = parse::attr::strip(["graphql_subscription", "graphql"], ast.attrs);
1818
return expand_on_impl::<Subscription>(
19-
Attr::from_attrs("graphql_subscription", &impl_attrs)?,
19+
Attr::from_attrs(["graphql_subscription", "graphql"], &impl_attrs)?,
2020
ast,
2121
);
2222
}

juniper_codegen/src/graphql_union/attr.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const ERR: diagnostic::Scope = diagnostic::Scope::UnionAttr;
2020
pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
2121
if let Ok(mut ast) = syn::parse2::<syn::ItemTrait>(body) {
2222
let trait_attrs = parse::attr::unite(("graphql_union", &attr_args), &ast.attrs);
23-
ast.attrs = parse::attr::strip("graphql_union", ast.attrs);
23+
ast.attrs = parse::attr::strip(["graphql_union", "graphql"], ast.attrs);
2424
return expand_on_trait(trait_attrs, ast);
2525
}
2626

@@ -35,7 +35,7 @@ fn expand_on_trait(
3535
attrs: Vec<syn::Attribute>,
3636
mut ast: syn::ItemTrait,
3737
) -> syn::Result<TokenStream> {
38-
let attr = Attr::from_attrs("graphql_union", &attrs)?;
38+
let attr = Attr::from_attrs(["graphql_union", "graphql"], &attrs)?;
3939

4040
let trait_span = ast.span();
4141
let trait_ident = &ast.ident;

juniper_codegen/src/graphql_union/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use crate::common::{
2323
attr::{err, OptionExt as _},
2424
ParseBufferExt as _,
2525
},
26-
scalar, Description, SpanContainer,
26+
scalar, AttrNames, Description, SpanContainer,
2727
};
2828

2929
/// Helper alias for the type of [`Attr::external_resolvers`] field.
@@ -167,10 +167,10 @@ impl Attr {
167167
})
168168
}
169169

170-
/// Parses [`Attr`] from the given multiple `name`d [`syn::Attribute`]s
171-
/// placed on a type definition.
172-
fn from_attrs(name: &str, attrs: &[syn::Attribute]) -> syn::Result<Self> {
173-
let mut meta = filter_attrs(name, attrs)
170+
/// Parses an [`Attr`] from the provided multiple [`syn::Attribute`]s with
171+
/// the specified `names`, placed on a trait or type definition.
172+
fn from_attrs(names: impl AttrNames, attrs: &[syn::Attribute]) -> syn::Result<Self> {
173+
let mut meta = filter_attrs(names, attrs)
174174
.map(|attr| attr.parse_args())
175175
.try_fold(Self::default(), |prev, curr| prev.try_merge(curr?))?;
176176

0 commit comments

Comments
 (0)