|
1 |
| -use proc_macro::{self, TokenStream}; |
| 1 | +use proc_macro::TokenStream; |
| 2 | +use proc_macro2::TokenStream as TokenStream2; |
2 | 3 | use quote::quote;
|
3 | 4 | use std::str::FromStr;
|
4 |
| -use syn::{parse::Parser, parse_macro_input, DeriveInput}; |
| 5 | +use syn::DeriveInput; |
5 | 6 |
|
| 7 | +/// Use this on a struct to generate a built adapter around it, including useful impls. |
| 8 | +/// |
| 9 | +/// # Examples |
| 10 | +/// ``` |
| 11 | +/// # use gateway_addon_rust::prelude::*; |
| 12 | +/// # use async_trait::async_trait; |
| 13 | +/// #[adapter] |
| 14 | +/// struct ExampleAdapter { foo: i32 } |
| 15 | +/// |
| 16 | +/// #[async_trait] |
| 17 | +/// impl Adapter for BuiltExampleAdapter { |
| 18 | +/// async fn on_unload(&mut self) -> Result<(), String> { |
| 19 | +/// println!("Foo: {}", self.foo); |
| 20 | +/// Ok(()) |
| 21 | +/// } |
| 22 | +/// } |
| 23 | +/// ``` |
| 24 | +/// will expand to |
| 25 | +/// ``` |
| 26 | +/// # use gateway_addon_rust::{prelude::*, adapter::AdapterHandleWrapper}; |
| 27 | +/// # use std::ops::{Deref, DerefMut}; |
| 28 | +/// # use async_trait::async_trait; |
| 29 | +/// struct ExampleAdapter { foo: i32 } |
| 30 | +/// |
| 31 | +/// struct BuiltExampleAdapter{ |
| 32 | +/// data: ExampleAdapter, |
| 33 | +/// adapter_handle: AdapterHandle |
| 34 | +/// } |
| 35 | +/// |
| 36 | +/// impl AdapterHandleWrapper for BuiltExampleAdapter { |
| 37 | +/// fn adapter_handle(&self) -> &AdapterHandle { |
| 38 | +/// &self.adapter_handle |
| 39 | +/// } |
| 40 | +/// fn adapter_handle_mut(&mut self) -> &mut AdapterHandle { |
| 41 | +/// &mut self.adapter_handle |
| 42 | +/// } |
| 43 | +/// } |
| 44 | +/// |
| 45 | +/// impl BuildAdapter for ExampleAdapter { |
| 46 | +/// type BuiltAdapter = BuiltExampleAdapter; |
| 47 | +/// fn build(data: Self, adapter_handle: AdapterHandle) -> Self::BuiltAdapter { |
| 48 | +/// BuiltExampleAdapter { data, adapter_handle } |
| 49 | +/// } |
| 50 | +/// } |
| 51 | +/// |
| 52 | +/// impl Deref for BuiltExampleAdapter { |
| 53 | +/// type Target = ExampleAdapter; |
| 54 | +/// fn deref(&self) -> &Self::Target { |
| 55 | +/// &self.data |
| 56 | +/// } |
| 57 | +/// } |
| 58 | +/// |
| 59 | +/// impl DerefMut for BuiltExampleAdapter { |
| 60 | +/// fn deref_mut(&mut self) -> &mut Self::Target { |
| 61 | +/// &mut self.data |
| 62 | +/// } |
| 63 | +/// } |
| 64 | +/// |
| 65 | +/// #[async_trait] |
| 66 | +/// impl Adapter for BuiltExampleAdapter { |
| 67 | +/// // ... |
| 68 | +/// } |
| 69 | +/// ``` |
6 | 70 | #[proc_macro_attribute]
|
7 | 71 | pub fn adapter(_args: TokenStream, input: TokenStream) -> TokenStream {
|
8 |
| - alter_struct(input, "adapter", "Adapter") |
| 72 | + apply_macro(input, "adapter", "Adapter") |
9 | 73 | }
|
10 | 74 |
|
11 | 75 | #[proc_macro_attribute]
|
12 | 76 | pub fn device(_args: TokenStream, input: TokenStream) -> TokenStream {
|
13 |
| - alter_struct(input, "device", "Device") |
| 77 | + apply_macro(input, "device", "Device") |
14 | 78 | }
|
15 | 79 |
|
16 |
| -fn alter_struct(input: TokenStream, name_snail_case: &str, name_camel_case: &str) -> TokenStream { |
17 |
| - let trait_handle_wrapper = proc_macro2::TokenStream::from_str(&format!( |
| 80 | +fn apply_macro(input: TokenStream, name_snail_case: &str, name_camel_case: &str) -> TokenStream { |
| 81 | + if let Ok(ast) = syn::parse2::<DeriveInput>(input.into()) { |
| 82 | + alter_struct(ast, name_snail_case, name_camel_case).into() |
| 83 | + } else { |
| 84 | + panic!("`{}` has to be used with structs", name_snail_case) |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +fn alter_struct(ast: DeriveInput, name_snail_case: &str, name_camel_case: &str) -> TokenStream2 { |
| 89 | + let trait_handle_wrapper = TokenStream2::from_str(&format!( |
18 | 90 | "gateway_addon_rust::{}::{}HandleWrapper",
|
19 | 91 | name_snail_case, name_camel_case
|
20 | 92 | ))
|
21 | 93 | .unwrap();
|
22 |
| - let struct_handle = proc_macro2::TokenStream::from_str(&format!( |
| 94 | + let trait_build = TokenStream2::from_str(&format!( |
| 95 | + "gateway_addon_rust::{}::Build{}", |
| 96 | + name_snail_case, name_camel_case |
| 97 | + )) |
| 98 | + .unwrap(); |
| 99 | + let struct_built = TokenStream2::from_str(&format!("Built{}", name_camel_case)).unwrap(); |
| 100 | + let struct_handle = TokenStream2::from_str(&format!( |
23 | 101 | "gateway_addon_rust::{}::{}Handle",
|
24 | 102 | name_snail_case, name_camel_case
|
25 | 103 | ))
|
26 | 104 | .unwrap();
|
27 |
| - let fn_handle = |
28 |
| - proc_macro2::TokenStream::from_str(&format!("{}_handle", name_snail_case)).unwrap(); |
29 |
| - let fn_handle_mut = |
30 |
| - proc_macro2::TokenStream::from_str(&format!("{}_handle_mut", name_snail_case)).unwrap(); |
| 105 | + let fn_handle = TokenStream2::from_str(&format!("{}_handle", name_snail_case)).unwrap(); |
| 106 | + let fn_handle_mut = TokenStream2::from_str(&format!("{}_handle_mut", name_snail_case)).unwrap(); |
31 | 107 |
|
32 |
| - let mut ast = parse_macro_input!(input as DeriveInput); |
33 |
| - if let syn::Data::Struct(ref mut struct_data) = &mut ast.data { |
34 |
| - let struct_name = ast.ident.clone(); |
35 |
| - let field_name = field_name(struct_data, name_snail_case); |
36 |
| - add_struct_field(struct_data, &field_name, struct_handle.clone()); |
| 108 | + let struct_name = ast.ident.clone(); |
| 109 | + let struct_built_name = TokenStream2::from_str(&format!("Built{}", struct_name)).unwrap(); |
37 | 110 |
|
38 |
| - quote! { |
39 |
| - #ast |
40 |
| - impl #trait_handle_wrapper for #struct_name { |
41 |
| - fn #fn_handle(&self) -> &#struct_handle { |
42 |
| - &self.#field_name |
43 |
| - } |
44 |
| - fn #fn_handle_mut(&mut self) -> &mut #struct_handle { |
45 |
| - &mut self.#field_name |
46 |
| - } |
| 111 | + quote! { |
| 112 | + #ast |
| 113 | + impl #trait_build for #struct_name { |
| 114 | + type #struct_built = #struct_built_name; |
| 115 | + fn build(data: Self, #fn_handle: #struct_handle) -> Self::#struct_built { |
| 116 | + #struct_built_name { data, #fn_handle } |
47 | 117 | }
|
48 |
| - impl std::ops::Deref for #struct_name { |
49 |
| - type Target = #struct_handle; |
50 |
| - fn deref(&self) -> &Self::Target { |
51 |
| - &self.#field_name |
52 |
| - } |
| 118 | + } |
| 119 | + struct #struct_built_name { |
| 120 | + data: #struct_name, |
| 121 | + #fn_handle: #struct_handle, |
| 122 | + } |
| 123 | + impl #trait_handle_wrapper for #struct_built_name { |
| 124 | + fn #fn_handle(&self) -> &#struct_handle { |
| 125 | + &self.#fn_handle |
53 | 126 | }
|
54 |
| - impl std::ops::DerefMut for #struct_name { |
55 |
| - fn deref_mut(&mut self) -> &mut Self::Target { |
56 |
| - &mut self.#field_name |
57 |
| - } |
| 127 | + fn #fn_handle_mut(&mut self) -> &mut #struct_handle { |
| 128 | + &mut self.#fn_handle |
58 | 129 | }
|
59 | 130 | }
|
60 |
| - .into() |
61 |
| - } else { |
62 |
| - panic!("`{}` has to be used with structs", name_snail_case) |
63 |
| - } |
64 |
| -} |
65 |
| - |
66 |
| -fn field_name(struct_data: &mut syn::DataStruct, identifier: &str) -> syn::Member { |
67 |
| - match &mut struct_data.fields { |
68 |
| - syn::Fields::Named(_) => syn::Member::Named(syn::Ident::new( |
69 |
| - &format!("{}_handle", identifier), |
70 |
| - proc_macro2::Span::call_site(), |
71 |
| - )), |
72 |
| - syn::Fields::Unnamed(fields) => syn::Member::Unnamed(syn::Index { |
73 |
| - index: fields.unnamed.len() as _, |
74 |
| - span: proc_macro2::Span::call_site(), |
75 |
| - }), |
76 |
| - syn::Fields::Unit => syn::Member::Unnamed(syn::Index { |
77 |
| - index: 0, |
78 |
| - span: proc_macro2::Span::call_site(), |
79 |
| - }), |
80 |
| - } |
81 |
| -} |
82 |
| - |
83 |
| -fn add_struct_field( |
84 |
| - struct_data: &mut syn::DataStruct, |
85 |
| - field_name: &syn::Member, |
86 |
| - field_type: proc_macro2::TokenStream, |
87 |
| -) { |
88 |
| - match &mut struct_data.fields { |
89 |
| - syn::Fields::Named(fields) => { |
90 |
| - fields.named.push( |
91 |
| - syn::Field::parse_named |
92 |
| - .parse2(quote! { #field_name: #field_type }) |
93 |
| - .unwrap(), |
94 |
| - ); |
95 |
| - } |
96 |
| - syn::Fields::Unnamed(fields) => { |
97 |
| - fields.unnamed.push( |
98 |
| - syn::Field::parse_unnamed |
99 |
| - .parse2(quote! { #field_type }) |
100 |
| - .unwrap(), |
101 |
| - ); |
| 131 | + impl std::ops::Deref for #struct_built_name { |
| 132 | + type Target = #struct_name; |
| 133 | + fn deref(&self) -> &Self::Target { |
| 134 | + &self.data |
| 135 | + } |
102 | 136 | }
|
103 |
| - syn::Fields::Unit => { |
104 |
| - let mut fields = syn::punctuated::Punctuated::new(); |
105 |
| - fields.push( |
106 |
| - syn::Field::parse_unnamed |
107 |
| - .parse2(quote! { #field_type }) |
108 |
| - .unwrap(), |
109 |
| - ); |
110 |
| - struct_data.fields = syn::Fields::Unnamed(syn::FieldsUnnamed { |
111 |
| - paren_token: syn::token::Paren { |
112 |
| - span: proc_macro2::Span::call_site(), |
113 |
| - }, |
114 |
| - unnamed: fields, |
115 |
| - }); |
| 137 | + impl std::ops::DerefMut for #struct_built_name { |
| 138 | + fn deref_mut(&mut self) -> &mut Self::Target { |
| 139 | + &mut self.data |
| 140 | + } |
116 | 141 | }
|
117 | 142 | }
|
118 | 143 | }
|
0 commit comments