juniper/juniper_codegen/src/enums.rs

180 lines
5.3 KiB
Rust
Raw Normal View History

use syn;
use syn::*;
use quote::Tokens;
use ::util::*;
#[derive(Default, Debug)]
struct EnumAttrs {
name: Option<String>,
description: Option<String>,
}
impl EnumAttrs {
fn from_input(input: &DeriveInput) -> EnumAttrs {
let mut res = EnumAttrs::default();
// 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;
}
panic!(format!(
"Unknown attribute for #[derive(GraphQLEnum)]: {:?}",
item));
}
}
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)]: {:?}",
item));
}
}
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");
},
};
// Parse attributes.
let ident = &ast.ident;
let attrs = EnumAttrs::from_input(ast);
let name = attrs.name.unwrap_or(ast.ident.to_string());
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.",
variant.ident));
}
let var_attrs = EnumVariantAttrs::from_input(variant);
let var_ident = &variant.ident;
// Build value.
let name = var_attrs.name.unwrap_or(variant.ident.to_string());
let descr = match var_attrs.description {
Some(s) => quote!{ Some(#s.to_string()) },
None => quote!{ None },
};
let depr = match var_attrs.deprecation {
Some(s) => quote!{ Some(#s.to_string()) },
None => quote!{ None },
};
let value = quote!{
::juniper::meta::EnumValue{
name: #name.to_string(),
description: #descr,
deprecation_reason: #depr,
},
};
values.push(value);
// Build resolve match clause.
let resolve = quote!{
&#ident::#var_ident => ::juniper::Value::String(#name.to_string()),
};
resolves.push(resolve);
// Buil from_input clause.
let from_input = quote!{
Some(#name) => Some(#ident::#var_ident),
};
from_inputs.push(from_input);
// Buil to_input clause.
let to_input = quote!{
&#ident::#var_ident =>
::juniper::InputValue::string(#name.to_string()),
};
to_inputs.push(to_input);
};
quote! {
impl ::juniper::GraphQLType for #ident {
type Context = ();
fn name() -> Option<&'static str> {
Some(#name)
}
fn meta<'r>(registry: &mut ::juniper::Registry<'r>) -> ::juniper::meta::MetaType<'r> {
registry.build_enum_type::<#ident>(&[
#(#values)*
])
.into_meta()
}
fn resolve(&self, _: Option<&[::juniper::Selection]>, _: &::juniper::Executor<Self::Context>) -> ::juniper::Value {
match self {
#(#resolves)*
}
}
}
impl ::juniper::FromInputValue for #ident {
fn from(v: &::juniper::InputValue) -> Option<#ident> {
match v.as_enum_value().or_else(|| v.as_string_value()) {
#(#from_inputs)*
_ => None,
}
}
}
impl ::juniper::ToInputValue for #ident {
fn to(&self) -> ::juniper::InputValue {
match self {
#(#to_inputs)*
}
}
}
}
}