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

This commit is contained in:
Kai Ren 2023-12-11 21:31:04 +01:00 committed by GitHub
parent c0e1b3eae3
commit 9420f3c19e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 140 additions and 76 deletions

View file

@ -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
}
}

View file

@ -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`.
@ -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()
}

View file

@ -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);
}
@ -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();
@ -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();

View file

@ -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`]
@ -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?))?;

View file

@ -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,
);
}
}

View file

@ -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
@ -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?))?;

View file

@ -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);
}
@ -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(),
@ -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);

View file

@ -20,7 +20,7 @@ use crate::common::{
attr::{err, OptionExt as _},
ParseBufferExt as _,
},
scalar, Description, SpanContainer,
scalar, AttrNames, Description, SpanContainer,
};
pub mod attr;
@ -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?))?;

View file

@ -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,
);
}

View file

@ -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);
}
@ -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;

View file

@ -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.
@ -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?))?;

View file

@ -674,7 +674,8 @@ pub fn derive_scalar(input: TokenStream) -> TokenStream {
/// # use juniper::graphql_scalar;
/// #
/// /// Doc comments are used for the GraphQL type description.
/// #[graphql_scalar(
/// #[graphql_scalar]
/// #[graphql(
/// // Custom GraphQL name.
/// name = "MyUserId",
/// // Description can also specified in the attribute.
@ -721,7 +722,8 @@ pub fn derive_scalar(input: TokenStream) -> TokenStream {
/// # use juniper::DefaultScalarValue as CustomScalarValue;
/// use juniper::{graphql_scalar, InputValue, ScalarValue, Value};
///
/// #[graphql_scalar(
/// #[graphql_scalar]
/// #[graphql(
/// with = date_scalar,
/// parse_token(String),
/// scalar = CustomScalarValue,
@ -897,7 +899,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
///
/// // NOTICE: By default a `CharacterValue` enum is generated by macro to represent values of this
/// // GraphQL interface.
/// #[graphql_interface(for = Human)] // enumerating all implementers is mandatory
/// #[graphql_interface]
/// #[graphql(for = Human)] // enumerating all implementers is mandatory
/// struct Character {
/// id: String,
/// }
@ -917,7 +920,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
///
/// // NOTICE: By default a `CharacterValue` enum is generated by macro to represent values of this
/// // GraphQL interface.
/// #[graphql_interface(for = Human)] // enumerating all implementers is mandatory
/// #[graphql_interface]
/// #[graphql(for = Human)] // enumerating all implementers is mandatory
/// trait Character {
/// fn id(&self) -> &str;
/// }
@ -953,7 +957,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// ```rust
/// # use juniper::graphql_interface;
/// #
/// #[graphql_interface(name = "Character", desc = "Possible episode characters.")]
/// #[graphql_interface]
/// #[graphql(name = "Character", desc = "Possible episode characters.")]
/// trait Chrctr {
/// #[graphql(name = "id", desc = "ID of the character.")]
/// #[graphql(deprecated = "Don't use it")]
@ -988,12 +993,14 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// # extern crate juniper;
/// use juniper::{graphql_interface, graphql_object, ID};
///
/// #[graphql_interface(for = [HumanValue, Luke])]
/// #[graphql_interface]
/// #[graphql(for = [HumanValue, Luke])]
/// struct Node {
/// id: ID,
/// }
///
/// #[graphql_interface(impl = NodeValue, for = Luke)]
/// #[graphql_interface]
/// #[graphql(impl = NodeValue, for = Luke)]
/// struct Human {
/// id: ID,
/// home_planet: String,
@ -1003,7 +1010,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// id: ID,
/// }
///
/// #[graphql_object(impl = [HumanValue, NodeValue])]
/// #[graphql_object]
/// #[graphql(impl = [HumanValue, NodeValue])]
/// impl Luke {
/// fn id(&self) -> &ID {
/// &self.id
@ -1043,23 +1051,27 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// # extern crate juniper;
/// use juniper::{graphql_interface, graphql_object, ID};
///
/// #[graphql_interface(for = [HumanValue, Luke])]
/// #[graphql_interface]
/// #[graphql(for = [HumanValue, Luke])]
/// struct Node {
/// id: ID,
/// }
///
/// #[graphql_interface(for = HumanConnectionValue)]
/// #[graphql_interface]
/// #[graphql(for = HumanConnectionValue)]
/// struct Connection {
/// nodes: Vec<NodeValue>,
/// }
///
/// #[graphql_interface(impl = NodeValue, for = Luke)]
/// #[graphql_interface]
/// #[graphql(impl = NodeValue, for = Luke)]
/// struct Human {
/// id: ID,
/// home_planet: String,
/// }
///
/// #[graphql_interface(impl = ConnectionValue)]
/// #[graphql_interface]
/// #[graphql(impl = ConnectionValue)]
/// struct HumanConnection {
/// nodes: Vec<HumanValue>,
/// // ^^^^^^^^^^ notice not `NodeValue`
@ -1072,7 +1084,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// id: ID,
/// }
///
/// #[graphql_object(impl = [HumanValue, NodeValue])]
/// #[graphql_object]
/// #[graphql(impl = [HumanValue, NodeValue])]
/// impl Luke {
/// fn id(&self) -> &ID {
/// &self.id
@ -1109,7 +1122,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// ```rust
/// # use juniper::{graphql_interface, graphql_object};
/// #
/// #[graphql_interface(for = Human, rename_all = "none")] // disables renaming
/// #[graphql_interface]
/// #[graphql(for = Human, rename_all = "none")] // disables renaming
/// trait Character {
/// // NOTICE: In the generated GraphQL schema this field and its argument
/// // will be `detailed_info` and `info_kind`.
@ -1121,7 +1135,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// home_planet: String,
/// }
///
/// #[graphql_object(impl = CharacterValue, rename_all = "none")]
/// #[graphql_object]
/// #[graphql(impl = CharacterValue, rename_all = "none")]
/// impl Human {
/// fn id(&self) -> &str {
/// &self.id
@ -1179,7 +1194,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// }
/// impl juniper::Context for Database {}
///
/// #[graphql_interface(for = [Human, Droid], Context = Database)]
/// #[graphql_interface]
/// #[graphql(for = [Human, Droid], Context = Database)]
/// trait Character {
/// fn id<'db>(&self, ctx: &'db Database) -> Option<&'db str>;
/// fn info<'db>(&self, #[graphql(context)] db: &'db Database) -> Option<&'db str>;
@ -1189,7 +1205,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// id: String,
/// home_planet: String,
/// }
/// #[graphql_object(impl = CharacterValue, Context = Database)]
/// #[graphql_object]
/// #[graphql(impl = CharacterValue, Context = Database)]
/// impl Human {
/// fn id<'db>(&self, context: &'db Database) -> Option<&'db str> {
/// context.humans.get(&self.id).map(|h| h.id.as_str())
@ -1206,7 +1223,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// id: String,
/// primary_function: String,
/// }
/// #[graphql_object(impl = CharacterValue, Context = Database)]
/// #[graphql_object]
/// #[graphql(impl = CharacterValue, Context = Database)]
/// impl Droid {
/// fn id<'db>(&self, ctx: &'db Database) -> Option<&'db str> {
/// ctx.droids.get(&self.id).map(|h| h.id.as_str())
@ -1231,8 +1249,9 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// ```rust
/// # use juniper::{graphql_interface, graphql_object, Executor, LookAheadMethods as _, ScalarValue};
/// #
/// #[graphql_interface]
/// // NOTICE: Specifying `ScalarValue` as existing type parameter.
/// #[graphql_interface(for = Human, scalar = S)]
/// #[graphql(for = Human, scalar = S)]
/// trait Character<S: ScalarValue> {
/// fn id<'a>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str;
///
@ -1246,7 +1265,8 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// id: String,
/// name: String,
/// }
/// #[graphql_object(scalar = S: ScalarValue, impl = CharacterValue<S>)]
/// #[graphql_object]
/// #[graphql(scalar = S: ScalarValue, impl = CharacterValue<S>)]
/// impl Human {
/// async fn id<'a, S>(&self, executor: &'a Executor<'_, '_, (), S>) -> &'a str
/// where
@ -1273,8 +1293,9 @@ pub fn derive_scalar_value(input: TokenStream) -> TokenStream {
/// ```rust
/// # use juniper::{graphql_interface, DefaultScalarValue, GraphQLObject};
/// #
/// #[graphql_interface]
/// // NOTICE: Removing `Scalar` argument will fail compilation.
/// #[graphql_interface(for = Human, scalar = DefaultScalarValue)]
/// #[graphql(for = Human, scalar = DefaultScalarValue)]
/// trait Character {
/// fn id(&self) -> &str;
/// }
@ -1585,7 +1606,8 @@ pub fn derive_object(body: TokenStream) -> TokenStream {
/// #
/// struct HumanWithAttrs;
///
/// #[graphql_object(
/// #[graphql_object]
/// #[graphql(
/// // Rename the type for GraphQL by specifying the name here.
/// name = "Human",
/// // You may also specify a description here.
@ -1643,7 +1665,8 @@ pub fn derive_object(body: TokenStream) -> TokenStream {
/// #
/// struct Query;
///
/// #[graphql_object(rename_all = "none")] // disables renaming
/// #[graphql_object]
/// #[graphql(rename_all = "none")] // disables renaming
/// impl Query {
/// // NOTICE: In the generated GraphQL schema this field will be available
/// // as `api_version`.
@ -1708,7 +1731,8 @@ pub fn derive_object(body: TokenStream) -> TokenStream {
/// home_planet: String,
/// }
///
/// #[graphql_object(context = Database)]
/// #[graphql_object]
/// #[graphql(context = Database)]
/// impl Human {
/// fn id<'db>(&self, context: &'db Database) -> Option<&'db str> {
/// context.humans.get(&self.id).map(|h| h.id.as_str())
@ -1736,9 +1760,10 @@ pub fn derive_object(body: TokenStream) -> TokenStream {
/// name: String,
/// }
///
/// #[graphql_object]
/// // NOTICE: Specifying `ScalarValue` as custom named type parameter.
/// // Its name should be similar to the one used in methods.
/// #[graphql_object(scalar = S: ScalarValue)]
/// #[graphql(scalar = S: ScalarValue)]
/// impl Human {
/// async fn id<'a, S: ScalarValue>(
/// &self,
@ -1769,8 +1794,9 @@ pub fn derive_object(body: TokenStream) -> TokenStream {
/// #
/// struct Human(String);
///
/// #[graphql_object]
/// // NOTICE: Removing `scalar` argument will fail compilation.
/// #[graphql_object(scalar = DefaultScalarValue)]
/// #[graphql(scalar = DefaultScalarValue)]
/// impl Human {
/// fn id(&self) -> &str {
/// &self.0
@ -2232,7 +2258,8 @@ pub fn derive_union(body: TokenStream) -> TokenStream {
/// # primary_function: String,
/// # }
/// #
/// #[graphql_union(name = "Character", desc = "Possible episode characters.")]
/// #[graphql_union]
/// #[graphql(name = "Character", desc = "Possible episode characters.")]
/// trait Chrctr {
/// fn as_human(&self) -> Option<&Human> { None }
/// fn as_droid(&self) -> Option<&Droid> { None }
@ -2247,7 +2274,8 @@ pub fn derive_union(body: TokenStream) -> TokenStream {
///
/// // NOTICE: `description` argument takes precedence over Rust docs.
/// /// Not a GraphQL description anymore.
/// #[graphql_union(description = "Possible episode characters.")]
/// #[graphql_union]
/// #[graphql(description = "Possible episode characters.")]
/// trait CharacterWithDescription {
/// fn as_human(&self) -> Option<&Human> { None }
/// fn as_droid(&self) -> Option<&Droid> { None }
@ -2293,7 +2321,8 @@ pub fn derive_union(body: TokenStream) -> TokenStream {
/// }
/// impl juniper::Context for Database {}
///
/// #[graphql_union(Context = Database)]
/// #[graphql_union]
/// #[graphql(context = Database)]
/// trait Character {
/// fn as_human<'db>(&self, ctx: &'db Database) -> Option<&'db Human> { None }
/// fn as_droid<'db>(&self, ctx: &'db Database) -> Option<&'db Droid> { None }
@ -2336,8 +2365,9 @@ pub fn derive_union(body: TokenStream) -> TokenStream {
/// primary_function: String,
/// }
///
/// // NOTICE: Removing `Scalar` argument will fail compilation.
/// #[graphql_union(scalar = DefaultScalarValue)]
/// // NOTICE: Removing `scalar` argument will fail compilation.
/// #[graphql_union]
/// #[graphql(scalar = DefaultScalarValue)]
/// trait Character {
/// fn as_human(&self) -> Option<&Human> { None }
/// fn as_droid(&self) -> Option<&Droid> { None }
@ -2413,8 +2443,9 @@ pub fn derive_union(body: TokenStream) -> TokenStream {
/// }
/// impl juniper::Context for Database {}
///
/// #[graphql_union(Context = Database)]
/// #[graphql_union(
/// #[graphql_union]
/// #[graphql(context = Database)]
/// #[graphql(
/// on Human = DynCharacter::get_human,
/// on Droid = get_droid,
/// )]