1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! GraphQL enum type generation.
//!
//! Generates a GraphQL Enum type definition from a Rust enum.
//!
//! # Example
//!
//! ```ignore
//! use server_less::graphql_enum;
//!
//! #[graphql_enum]
//! #[derive(Clone, Debug)]
//! enum Status {
//! /// User is active
//! Active,
//! /// User is inactive
//! Inactive,
//! /// Awaiting approval
//! Pending,
//! }
//!
//! // Register with #[graphql]:
//! #[graphql(enums(Status))]
//! impl MyService {
//! pub fn get_status(&self) -> Status { Status::Active }
//! }
//! ```
use heck::ToShoutySnakeCase;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::ItemEnum;
pub(crate) fn expand_graphql_enum(item: ItemEnum) -> syn::Result<TokenStream2> {
let enum_name = &item.ident;
let enum_name_str = enum_name.to_string();
let mut variant_registrations = Vec::new();
let mut to_value_arms = Vec::new();
for variant in &item.variants {
// Only support unit variants (no fields)
if !variant.fields.is_empty() {
return Err(syn::Error::new_spanned(
variant,
"GraphQL enums only support unit variants (no fields)\n\
\n\
Example:\n\
#[graphql_enum]\n\
enum Status {\n\
Active,\n\
Inactive,\n\
}",
));
}
let variant_name = &variant.ident;
let graphql_name = variant_name.to_string().to_shouty_snake_case();
// Extract doc comment
let doc = variant
.attrs
.iter()
.filter_map(|attr| {
if attr.path().is_ident("doc")
&& let syn::Meta::NameValue(nv) = &attr.meta
&& let syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(s),
..
}) = &nv.value
{
return Some(s.value().trim().to_string());
}
None
})
.collect::<Vec<_>>()
.join(" ");
let registration = if doc.is_empty() {
quote! {
.item(::async_graphql::dynamic::EnumItem::new(#graphql_name))
}
} else {
quote! {
.item(::async_graphql::dynamic::EnumItem::new(#graphql_name).description(#doc))
}
};
variant_registrations.push(registration);
// to_value arm: variant => "GRAPHQL_NAME"
to_value_arms.push(quote! {
#enum_name::#variant_name => ::async_graphql::Value::Enum(
::async_graphql::Name::new(#graphql_name)
),
});
}
Ok(quote! {
#item
impl #enum_name {
/// Get the GraphQL Enum type definition for this enum.
///
/// Used by `#[graphql(enums(...))]` to register the enum in the schema.
pub fn __graphql_enum_type() -> ::async_graphql::dynamic::Enum {
::async_graphql::dynamic::Enum::new(#enum_name_str)
#(#variant_registrations)*
}
/// Convert this enum value to a GraphQL Value.
pub fn __to_graphql_value(&self) -> ::async_graphql::Value {
match self {
#(#to_value_arms)*
}
}
}
})
}