Skip to content

Allow top-level #[graphql] attribute in attribute macros #1232

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions juniper_codegen/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,51 @@ pub(crate) mod rename;
pub(crate) mod scalar;
mod span_container;

use std::slice;

pub(crate) use self::{description::Description, span_container::SpanContainer};

/// Checks whether the specified [`syn::Path`] equals to one-segment string
/// `value`.
pub(crate) fn path_eq_single(path: &syn::Path, value: &str) -> bool {
path.segments.len() == 1 && path.segments[0].ident == value
/// Checks whether the specified [`syn::Path`] equals to one of specified one-segment
/// [`AttrNames::values`].
pub(crate) fn path_eq_single(path: &syn::Path, names: impl AttrNames) -> bool {
path.segments.len() == 1
&& names
.values()
.iter()
.any(|name| path.segments[0].ident == name)
}

/// Filters the provided [`syn::Attribute`] to contain only ones with the
/// specified `name`.
pub(crate) fn filter_attrs<'a>(
name: &'a str,
names: impl AttrNames + 'a,
attrs: &'a [syn::Attribute],
) -> impl Iterator<Item = &'a syn::Attribute> + 'a {
attrs
.iter()
.filter(move |attr| path_eq_single(attr.path(), name))
.filter(move |attr| path_eq_single(attr.path(), names))
}

/// Input-type polymorphism helper for checking names of multiple attribute names.
pub(crate) trait AttrNames: Copy {
/// Returns values to be checked.
fn values(&self) -> &[&str];
}

impl AttrNames for &str {
fn values(&self) -> &[&str] {
slice::from_ref(self)
}
}

impl AttrNames for &[&str] {
fn values(&self) -> &[&str] {
self
}
}

impl<const N: usize> AttrNames for [&str; N] {
fn values(&self) -> &[&str] {
self
}
}
6 changes: 3 additions & 3 deletions juniper_codegen/src/common/parse/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use proc_macro2::{Span, TokenStream};
use syn::parse_quote;

use crate::common::path_eq_single;
use crate::common::{path_eq_single, AttrNames};

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

Expand Down
8 changes: 4 additions & 4 deletions juniper_codegen/src/graphql_interface/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ const ERR: diagnostic::Scope = diagnostic::Scope::InterfaceAttr;
pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
if let Ok(mut ast) = syn::parse2::<syn::ItemTrait>(body.clone()) {
let trait_attrs = parse::attr::unite(("graphql_interface", &attr_args), &ast.attrs);
ast.attrs = parse::attr::strip("graphql_interface", ast.attrs);
ast.attrs = parse::attr::strip(["graphql_interface", "graphql"], ast.attrs);
return expand_on_trait(trait_attrs, ast);
}
if let Ok(mut ast) = syn::parse2::<syn::DeriveInput>(body) {
let trait_attrs = parse::attr::unite(("graphql_interface", &attr_args), &ast.attrs);
ast.attrs = parse::attr::strip("graphql_interface", ast.attrs);
ast.attrs = parse::attr::strip(["graphql_interface", "graphql"], ast.attrs);
return expand_on_derive_input(trait_attrs, ast);
}

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

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

let struct_ident = &ast.ident;
let struct_span = ast.span();
Expand Down
10 changes: 5 additions & 5 deletions juniper_codegen/src/graphql_interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::common::{
attr::{err, OptionExt as _},
GenericsExt as _, ParseBufferExt as _,
},
rename, scalar, Description, SpanContainer,
rename, scalar, AttrNames, Description, SpanContainer,
};

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

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

Expand Down
7 changes: 5 additions & 2 deletions juniper_codegen/src/graphql_object/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,11 @@ pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStr
if let Ok(mut ast) = syn::parse2::<syn::ItemImpl>(body) {
if ast.trait_.is_none() {
let impl_attrs = parse::attr::unite(("graphql_object", &attr_args), &ast.attrs);
ast.attrs = parse::attr::strip("graphql_object", ast.attrs);
return expand_on_impl::<Query>(Attr::from_attrs("graphql_object", &impl_attrs)?, ast);
ast.attrs = parse::attr::strip(["graphql_object", "graphql"], ast.attrs);
return expand_on_impl::<Query>(
Attr::from_attrs(["graphql_object", "graphql"], &impl_attrs)?,
ast,
);
}
}

Expand Down
10 changes: 5 additions & 5 deletions juniper_codegen/src/graphql_object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::common::{
attr::{err, OptionExt as _},
GenericsExt as _, ParseBufferExt as _, TypeExt,
},
rename, scalar, Description, SpanContainer,
rename, scalar, AttrNames, Description, SpanContainer,
};

/// Available arguments behind `#[graphql]` (or `#[graphql_object]`) attribute
Expand Down Expand Up @@ -181,10 +181,10 @@ impl Attr {
})
}

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

Expand Down
8 changes: 4 additions & 4 deletions juniper_codegen/src/graphql_scalar/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ const ERR: diagnostic::Scope = diagnostic::Scope::ScalarAttr;
pub(crate) fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
if let Ok(mut ast) = syn::parse2::<syn::ItemType>(body.clone()) {
let attrs = parse::attr::unite(("graphql_scalar", &attr_args), &ast.attrs);
ast.attrs = parse::attr::strip("graphql_scalar", ast.attrs);
ast.attrs = parse::attr::strip(["graphql_scalar", "graphql"], ast.attrs);
return expand_on_type_alias(attrs, ast);
} else if let Ok(mut ast) = syn::parse2::<syn::DeriveInput>(body) {
let attrs = parse::attr::unite(("graphql_scalar", &attr_args), &ast.attrs);
ast.attrs = parse::attr::strip("graphql_scalar", ast.attrs);
ast.attrs = parse::attr::strip(["graphql_scalar", "graphql"], ast.attrs);
return expand_on_derive_input(attrs, ast);
}

Expand All @@ -35,7 +35,7 @@ fn expand_on_type_alias(
attrs: Vec<syn::Attribute>,
ast: syn::ItemType,
) -> syn::Result<TokenStream> {
let attr = Attr::from_attrs("graphql_scalar", &attrs)?;
let attr = Attr::from_attrs(["graphql_scalar", "graphql"], &attrs)?;
if attr.transparent {
return Err(ERR.custom_error(
ast.span(),
Expand Down Expand Up @@ -73,7 +73,7 @@ fn expand_on_derive_input(
attrs: Vec<syn::Attribute>,
ast: syn::DeriveInput,
) -> syn::Result<TokenStream> {
let attr = Attr::from_attrs("graphql_scalar", &attrs)?;
let attr = Attr::from_attrs(["graphql_scalar", "graphql"], &attrs)?;
let methods = parse_derived_methods(&ast, &attr)?;
let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics);

Expand Down
10 changes: 5 additions & 5 deletions juniper_codegen/src/graphql_scalar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::common::{
attr::{err, OptionExt as _},
ParseBufferExt as _,
},
scalar, Description, SpanContainer,
scalar, AttrNames, Description, SpanContainer,
};

pub mod attr;
Expand Down Expand Up @@ -240,10 +240,10 @@ impl Attr {
})
}

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

Expand Down
4 changes: 2 additions & 2 deletions juniper_codegen/src/graphql_subscription/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStr
if let Ok(mut ast) = syn::parse2::<syn::ItemImpl>(body) {
if ast.trait_.is_none() {
let impl_attrs = parse::attr::unite(("graphql_subscription", &attr_args), &ast.attrs);
ast.attrs = parse::attr::strip("graphql_subscription", ast.attrs);
ast.attrs = parse::attr::strip(["graphql_subscription", "graphql"], ast.attrs);
return expand_on_impl::<Subscription>(
Attr::from_attrs("graphql_subscription", &impl_attrs)?,
Attr::from_attrs(["graphql_subscription", "graphql"], &impl_attrs)?,
ast,
);
}
Expand Down
4 changes: 2 additions & 2 deletions juniper_codegen/src/graphql_union/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const ERR: diagnostic::Scope = diagnostic::Scope::UnionAttr;
pub fn expand(attr_args: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
if let Ok(mut ast) = syn::parse2::<syn::ItemTrait>(body) {
let trait_attrs = parse::attr::unite(("graphql_union", &attr_args), &ast.attrs);
ast.attrs = parse::attr::strip("graphql_union", ast.attrs);
ast.attrs = parse::attr::strip(["graphql_union", "graphql"], ast.attrs);
return expand_on_trait(trait_attrs, ast);
}

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

let trait_span = ast.span();
let trait_ident = &ast.ident;
Expand Down
10 changes: 5 additions & 5 deletions juniper_codegen/src/graphql_union/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::common::{
attr::{err, OptionExt as _},
ParseBufferExt as _,
},
scalar, Description, SpanContainer,
scalar, AttrNames, Description, SpanContainer,
};

/// Helper alias for the type of [`Attr::external_resolvers`] field.
Expand Down Expand Up @@ -167,10 +167,10 @@ impl Attr {
})
}

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

Expand Down
Loading