async_graphql_relay_derive/
lib.rs1use darling::FromDeriveInput;
2use proc_macro::TokenStream;
3use syn::{parse_macro_input, Data, DeriveInput};
4
5#[macro_use]
6extern crate quote;
7extern crate proc_macro;
8
9#[derive(FromDeriveInput, Default)]
10#[darling(default, attributes(relay))]
11struct RelayNodeObjectAttributes {
12 node_suffix: Option<String>,
13}
14
15#[proc_macro_derive(RelayNodeObject, attributes(relay))]
27pub fn derive_relay_node_object(input: TokenStream) -> TokenStream {
28 let input = parse_macro_input!(input);
29 let attrs = RelayNodeObjectAttributes::from_derive_input(&input)
30 .expect("Error parsing 'RelayNodeObject' macro options!");
31 let DeriveInput { ident, data, .. } = input;
32
33 if !matches!(data, Data::Struct(_)) {
34 panic!("The 'RelayNodeObject' macro can only be used on structs!");
35 }
36
37 let value = if let Some(node_suffix) = attrs.node_suffix {
38 node_suffix
39 } else {
40 ident.to_string()
41 };
42
43 quote! {
44 impl async_graphql_relay::RelayNodeStruct for #ident {
45 const ID_SUFFIX: &'static str = #value;
46 }
47 }
48 .into()
49}
50
51#[proc_macro_derive(RelayInterface)]
63pub fn derive_relay_interface(input: TokenStream) -> TokenStream {
64 let DeriveInput { ident, data, .. } = parse_macro_input!(input);
65
66 let ident = format_ident!("{}GlobalID", ident);
67 let impls;
68 let node_matchers;
69 if let Data::Enum(data) = &data {
70 impls = data.variants.iter().map(|variant| {
71 let variant_ident = &variant.ident;
72 quote! {
73 impl std::convert::From<&async_graphql_relay::RelayNodeID<#variant_ident>> for #ident {
74 fn from(t: &async_graphql_relay::RelayNodeID<#variant_ident>) -> Self {
75 #ident(String::from(t))
76 }
77 }
78 }
79 });
80
81 node_matchers = data.variants.iter().map(|variant| {
82 let variant_ident = &variant.ident;
83 quote! {
84 <#variant_ident as async_graphql_relay::RelayNodeStruct>::ID_SUFFIX => {
85 <#variant_ident as async_graphql_relay::RelayNode>::get(
86 ctx,
87 async_graphql_relay::RelayNodeID::<#variant_ident>::new_from_relay_id(
88 relay_id.to_string(),
89 )?,
90 )
91 .await?
92 .ok_or_else(|| async_graphql::Error::new("A node with the specified id could not be found!"))
93 }
94 }
95 });
96 } else {
97 panic!("The 'RelayNodeObject' macro can only be used on enums!");
98 }
99
100 quote! {
101 #[derive(Clone, Debug)]
102 pub struct #ident(String);
103
104 #(#impls)*
105
106 #[async_graphql::Scalar(name = "RelayNodeID")]
107 impl async_graphql::ScalarType for #ident {
108 fn parse(value: async_graphql::Value) -> async_graphql::InputValueResult<Self> {
109 unimplemented!();
110 }
111
112 fn to_value(&self) -> async_graphql::Value {
113 async_graphql::Value::String(self.0.clone())
114 }
115 }
116
117 #[async_graphql_relay::_async_trait]
118 impl async_graphql_relay::RelayNodeInterface for Node {
119 async fn fetch_node(ctx: async_graphql_relay::RelayContext, relay_id: String) -> Result<Self, async_graphql::Error> {
120 if relay_id.len() < 32 {
121 return Err(async_graphql::Error::new("Invalid id provided to node query!"));
122 }
123 let (_, suffix) = relay_id.split_at(32);
124 match suffix {
125 #(#node_matchers)*
126 _ => Err(async_graphql::Error::new("A node with the specified id could not be found!")),
127 }
128 }
129 }
130 }
131 .into()
132}
133
134