2017-06-24 06:23:20 -05:00
|
|
|
use syn;
|
|
|
|
use syn::*;
|
|
|
|
use quote::Tokens;
|
|
|
|
|
2017-08-06 13:48:46 -05:00
|
|
|
use util::*;
|
2017-06-24 06:23:20 -05:00
|
|
|
|
|
|
|
|
|
|
|
#[derive(Default, Debug)]
|
|
|
|
struct EnumAttrs {
|
|
|
|
name: Option<String>,
|
|
|
|
description: Option<String>,
|
2017-12-02 05:42:07 -06:00
|
|
|
internal: bool,
|
2017-06-24 06:23:20 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl EnumAttrs {
|
|
|
|
fn from_input(input: &DeriveInput) -> EnumAttrs {
|
2017-12-02 05:42:07 -06:00
|
|
|
let mut res = EnumAttrs{
|
|
|
|
name: None,
|
|
|
|
description: None,
|
|
|
|
/// Flag to specify whether the calling crate is the "juniper" crate itself.
|
|
|
|
internal: false,
|
|
|
|
};
|
2017-06-24 06:23:20 -05:00
|
|
|
|
|
|
|
// Check attributes for name and description.
|
|
|
|
if let Some(items) = get_graphl_attr(&input.attrs) {
|
|
|
|
for item in items {
|
|
|
|
if let Some(val) = keyed_item_value(item, "name", true) {
|
|
|
|
res.name = Some(val);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if let Some(val) = keyed_item_value(item, "description", true) {
|
|
|
|
res.description = Some(val);
|
|
|
|
continue;
|
|
|
|
}
|
2017-12-02 05:42:07 -06:00
|
|
|
match item {
|
|
|
|
&NestedMetaItem::MetaItem(MetaItem::Word(ref ident)) => {
|
|
|
|
if ident == "_internal" {
|
|
|
|
res.internal = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {},
|
|
|
|
}
|
2017-06-24 06:23:20 -05:00
|
|
|
panic!(format!(
|
|
|
|
"Unknown attribute for #[derive(GraphQLEnum)]: {:?}",
|
2017-08-06 13:48:46 -05:00
|
|
|
item
|
|
|
|
));
|
2017-06-24 06:23:20 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
struct EnumVariantAttrs {
|
|
|
|
name: Option<String>,
|
|
|
|
description: Option<String>,
|
|
|
|
deprecation: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl EnumVariantAttrs {
|
|
|
|
fn from_input(variant: &Variant) -> EnumVariantAttrs {
|
|
|
|
let mut res = EnumVariantAttrs::default();
|
|
|
|
|
|
|
|
// Check attributes for name and description.
|
|
|
|
if let Some(items) = get_graphl_attr(&variant.attrs) {
|
|
|
|
for item in items {
|
|
|
|
if let Some(val) = keyed_item_value(item, "name", true) {
|
|
|
|
res.name = Some(val);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if let Some(val) = keyed_item_value(item, "description", true) {
|
|
|
|
res.description = Some(val);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if let Some(val) = keyed_item_value(item, "deprecated", true) {
|
|
|
|
res.deprecation = Some(val);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
panic!(format!(
|
|
|
|
"Unknown attribute for #[derive(GraphQLEnum)]: {:?}",
|
2017-08-06 13:48:46 -05:00
|
|
|
item
|
|
|
|
));
|
2017-06-24 06:23:20 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
res
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub fn impl_enum(ast: &syn::DeriveInput) -> Tokens {
|
|
|
|
let variants = match ast.body {
|
|
|
|
Body::Enum(ref var) => var,
|
|
|
|
Body::Struct(_) => {
|
|
|
|
panic!("#[derive(GraphlQLEnum)] may only be applied to enums, not to structs");
|
2017-08-06 13:48:46 -05:00
|
|
|
}
|
2017-06-24 06:23:20 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
// Parse attributes.
|
|
|
|
let ident = &ast.ident;
|
|
|
|
let attrs = EnumAttrs::from_input(ast);
|
|
|
|
let name = attrs.name.unwrap_or(ast.ident.to_string());
|
|
|
|
|
2017-08-02 21:18:11 -05:00
|
|
|
let meta_description = match attrs.description {
|
|
|
|
Some(descr) => quote!{ let meta = meta.description(#descr); },
|
|
|
|
None => quote!{ let meta = meta; },
|
|
|
|
};
|
|
|
|
|
2017-06-24 06:23:20 -05:00
|
|
|
let mut values = Vec::<Tokens>::new();
|
|
|
|
let mut resolves = Vec::<Tokens>::new();
|
|
|
|
let mut from_inputs = Vec::<Tokens>::new();
|
|
|
|
let mut to_inputs = Vec::<Tokens>::new();
|
|
|
|
|
|
|
|
for variant in variants {
|
|
|
|
if variant.data != VariantData::Unit {
|
|
|
|
panic!(format!(
|
|
|
|
"Invalid enum variant {}.\nGraphQL enums may only contain unit variants.",
|
2017-08-06 13:48:46 -05:00
|
|
|
variant.ident
|
|
|
|
));
|
2017-06-24 06:23:20 -05:00
|
|
|
}
|
2017-06-24 13:19:06 -05:00
|
|
|
let var_attrs = EnumVariantAttrs::from_input(variant);
|
|
|
|
let var_ident = &variant.ident;
|
2017-06-24 06:23:20 -05:00
|
|
|
|
|
|
|
// Build value.
|
2017-08-06 13:48:46 -05:00
|
|
|
let name = var_attrs
|
|
|
|
.name
|
|
|
|
.unwrap_or(variant.ident.as_ref().to_uppercase());
|
2017-06-24 13:19:06 -05:00
|
|
|
let descr = match var_attrs.description {
|
2017-06-24 06:23:20 -05:00
|
|
|
Some(s) => quote!{ Some(#s.to_string()) },
|
|
|
|
None => quote!{ None },
|
|
|
|
};
|
2017-06-24 13:19:06 -05:00
|
|
|
let depr = match var_attrs.deprecation {
|
2017-06-24 06:23:20 -05:00
|
|
|
Some(s) => quote!{ Some(#s.to_string()) },
|
|
|
|
None => quote!{ None },
|
|
|
|
};
|
|
|
|
let value = quote!{
|
2017-12-02 05:42:07 -06:00
|
|
|
_juniper::meta::EnumValue{
|
2017-06-24 06:23:20 -05:00
|
|
|
name: #name.to_string(),
|
|
|
|
description: #descr,
|
|
|
|
deprecation_reason: #depr,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
values.push(value);
|
|
|
|
|
|
|
|
// Build resolve match clause.
|
|
|
|
let resolve = quote!{
|
2017-12-02 05:42:07 -06:00
|
|
|
&#ident::#var_ident => _juniper::Value::String(#name.to_string()),
|
2017-06-24 06:23:20 -05:00
|
|
|
};
|
|
|
|
resolves.push(resolve);
|
|
|
|
|
|
|
|
// Buil from_input clause.
|
|
|
|
let from_input = quote!{
|
2017-06-24 13:19:06 -05:00
|
|
|
Some(#name) => Some(#ident::#var_ident),
|
2017-06-24 06:23:20 -05:00
|
|
|
};
|
|
|
|
from_inputs.push(from_input);
|
|
|
|
|
|
|
|
// Buil to_input clause.
|
|
|
|
let to_input = quote!{
|
2017-06-24 13:19:06 -05:00
|
|
|
&#ident::#var_ident =>
|
2017-12-02 05:42:07 -06:00
|
|
|
_juniper::InputValue::string(#name.to_string()),
|
2017-06-24 06:23:20 -05:00
|
|
|
};
|
|
|
|
to_inputs.push(to_input);
|
2017-08-06 13:48:46 -05:00
|
|
|
}
|
2017-06-24 06:23:20 -05:00
|
|
|
|
2017-12-02 05:42:07 -06:00
|
|
|
let body = quote! {
|
|
|
|
impl _juniper::GraphQLType for #ident {
|
2017-06-24 06:23:20 -05:00
|
|
|
type Context = ();
|
2017-07-12 08:42:54 -05:00
|
|
|
type TypeInfo = ();
|
2017-06-24 06:23:20 -05:00
|
|
|
|
2017-07-12 08:42:54 -05:00
|
|
|
fn name(_: &()) -> Option<&'static str> {
|
2017-06-24 06:23:20 -05:00
|
|
|
Some(#name)
|
|
|
|
}
|
|
|
|
|
2017-12-02 05:42:07 -06:00
|
|
|
fn meta<'r>(_: &(), registry: &mut _juniper::Registry<'r>) -> _juniper::meta::MetaType<'r> {
|
2017-07-12 08:42:54 -05:00
|
|
|
let meta = registry.build_enum_type::<#ident>(&(), &[
|
2017-06-24 06:23:20 -05:00
|
|
|
#(#values)*
|
2017-08-02 21:18:11 -05:00
|
|
|
]);
|
|
|
|
#meta_description
|
|
|
|
meta.into_meta()
|
2017-06-24 06:23:20 -05:00
|
|
|
}
|
|
|
|
|
2017-12-02 05:42:07 -06:00
|
|
|
fn resolve(&self, _: &(), _: Option<&[_juniper::Selection]>, _: &_juniper::Executor<Self::Context>) -> _juniper::Value {
|
2017-06-24 06:23:20 -05:00
|
|
|
match self {
|
|
|
|
#(#resolves)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-02 05:42:07 -06:00
|
|
|
impl _juniper::FromInputValue for #ident {
|
|
|
|
fn from_input_value(v: &_juniper::InputValue) -> Option<#ident> {
|
2017-06-24 06:23:20 -05:00
|
|
|
match v.as_enum_value().or_else(|| v.as_string_value()) {
|
|
|
|
#(#from_inputs)*
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-02 05:42:07 -06:00
|
|
|
impl _juniper::ToInputValue for #ident {
|
|
|
|
fn to_input_value(&self) -> _juniper::InputValue {
|
2017-06-24 06:23:20 -05:00
|
|
|
match self {
|
|
|
|
#(#to_inputs)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-12-02 05:42:07 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
let dummy_const = Ident::new(format!("_IMPL_GRAPHQLENUM_FOR_{}", ident));
|
|
|
|
|
|
|
|
// This ugly hack makes it possible to use the derive inside juniper itself.
|
|
|
|
// FIXME: Figure out a better way to do this!
|
|
|
|
let crate_reference = if attrs.internal {
|
|
|
|
quote! {
|
|
|
|
#[doc(hidden)]
|
|
|
|
mod _juniper {
|
|
|
|
pub use ::{
|
|
|
|
InputValue,
|
|
|
|
Value,
|
|
|
|
ToInputValue,
|
|
|
|
FromInputValue,
|
|
|
|
Executor,
|
|
|
|
Selection,
|
|
|
|
Registry,
|
|
|
|
GraphQLType,
|
|
|
|
meta
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
quote! {
|
|
|
|
extern crate juniper as _juniper;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let generated = quote! {
|
|
|
|
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
|
|
|
|
#[doc(hidden)]
|
|
|
|
const #dummy_const : () = {
|
|
|
|
#crate_reference
|
|
|
|
#body
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
generated
|
2017-06-24 06:23:20 -05:00
|
|
|
}
|