Skip to content

Commit

Permalink
Allow top-level #[graphql] attribute in attribute macros (graphql-r…
Browse files Browse the repository at this point in the history
  • Loading branch information
tyranron authored Dec 11, 2023
1 parent c0e1b3e commit 9420f3c
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 76 deletions.
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

0 comments on commit 9420f3c

Please sign in to comment.