maybe_fut_unwrap_derive/
lib.rs1#![crate_name = "maybe_fut_unwrap_derive"]
2#![crate_type = "lib"]
3
4#![doc(html_playground_url = "https://play.rust-lang.org")]
22#![doc(
23 html_favicon_url = "https://raw.githubusercontent.com/veeso/maybe-fut/main/assets/images/logo-128.png"
24)]
25#![doc(
26 html_logo_url = "https://raw.githubusercontent.com/veeso/maybe-fut/main/assets/images/logo-500.png"
27)]
28
29use proc_macro::TokenStream;
30use quote::quote;
31use syn::{Data, DeriveInput, Fields, parenthesized, parse_macro_input};
32
33#[proc_macro_derive(Unwrap, attributes(unwrap_types))]
34pub fn unwrap(item: TokenStream) -> TokenStream {
35 let input = parse_macro_input!(item as DeriveInput);
36 let struct_name = &input.ident;
37 let generics = &input.generics;
38 let fields = match input.data {
40 Data::Struct(ref data) => match data.fields {
41 Fields::Unnamed(ref fields) => &fields.unnamed,
42 Fields::Named(_) => panic!("Unwrap can only be derived for tuple structs"),
43 Fields::Unit => panic!("Unwrap can only be derived for tuple structs"),
44 },
45 _ => panic!("Unwrap can only be derived for structs"),
46 };
47
48 let parent_struct_field = match fields.len() {
50 1 => &fields[0],
51 _ => panic!("Unwrap can only be derived for structs with a single field"),
52 };
53
54 let field_type = match &parent_struct_field.ty {
56 syn::Type::Path(path) => path,
57 _ => panic!("Unwrap can only be derived for structs with a single field"),
58 };
59
60 let field_type_ident = &field_type.path.segments.last().unwrap().ident;
61
62 let mut std_mod: Option<syn::Type> = None;
63 let mut tokio_mod: Option<syn::Type> = None;
64 let mut tokio_gated: Option<syn::LitStr> = None;
65
66 for attr in &input.attrs {
67 if attr.path().is_ident("unwrap_types") {
68 attr.parse_nested_meta(|meta| {
69 if meta.path.is_ident("std") {
70 let content;
71 parenthesized!(content in meta.input);
72 std_mod = Some(content.parse::<syn::Type>().expect("std ident not a value"));
73 Ok(())
74 } else if meta.path.is_ident("tokio") {
75 let content;
76 parenthesized!(content in meta.input);
77 tokio_mod = Some(
78 content
79 .parse::<syn::Type>()
80 .expect("tokio ident not a value"),
81 );
82 Ok(())
83 } else if meta.path.is_ident("tokio_gated") {
84 let content;
85 parenthesized!(content in meta.input);
86 tokio_gated = Some(
87 content
88 .parse::<syn::LitStr>()
89 .expect("tokio_gated ident not a value"),
90 );
91 Ok(())
92 } else if meta.path.is_ident("unwrap_types") {
93 Ok(())
95 } else {
96 Err(meta.error("Expected #[unwrap_types]"))
97 }
98 })
99 .expect("Invalid syntax in #[unwrap_types]");
100 }
101 }
102
103 let std_inner_type = std_mod.expect("Missing `std` in #[unwrap_types]");
104 let tokio_inner_type = tokio_mod.expect("Missing `tokio` in #[unwrap_types]");
105 let tokio_gated = tokio_gated
106 .as_ref()
107 .expect("Missing `tokio_gated` in #[unwrap_types]");
108
109 let output = quote! {
110 const _: () = {
111 use crate::Unwrap;
112
113 impl #generics Unwrap for #struct_name #generics {
114 type StdImpl = #std_inner_type #generics;
115 #[cfg(feature = #tokio_gated)]
116 type TokioImpl = #tokio_inner_type #generics;
117 #[cfg(all(not(feature = #tokio_gated), feature = "tokio"))]
118 type TokioImpl = #std_inner_type #generics;
119
120
121 fn unwrap_std(self) -> Self::StdImpl {
122 match self {
123 #struct_name(#field_type_ident::Std(inner)) => inner,
124 _ => panic!("Expected Std variant"),
125 }
126 }
127
128 #[cfg(feature = #tokio_gated)]
129 fn unwrap_tokio(self) -> Self::TokioImpl {
130 match self {
131 #struct_name(#field_type_ident::Tokio(inner)) => inner,
132 _ => panic!("Expected Tokio variant"),
133 }
134 }
135
136 #[cfg(all(not(feature = #tokio_gated), feature = "tokio"))]
137 fn unwrap_tokio(self) -> Self::TokioImpl {
138 match self {
139 #struct_name(#field_type_ident::Std(inner)) => inner,
140 _ => panic!("Expected Std variant"),
141 }
142 }
143
144 fn unwrap_std_ref(&self) -> &Self::StdImpl {
145 match self {
146 #struct_name(#field_type_ident::Std(inner)) => inner,
147 _ => panic!("Expected Std variant"),
148 }
149 }
150
151 #[cfg(feature = #tokio_gated)]
152 fn unwrap_tokio_ref(&self) -> &Self::TokioImpl {
153 match self {
154 #struct_name(#field_type_ident::Tokio(inner)) => inner,
155 _ => panic!("Expected Tokio variant"),
156 }
157 }
158
159 #[cfg(all(not(feature = #tokio_gated), feature = "tokio"))]
160 fn unwrap_tokio_ref(&self) -> &Self::TokioImpl {
161 match self {
162 #struct_name(#field_type_ident::Std(inner)) => inner,
163 _ => panic!("Expected Std variant"),
164 }
165 }
166
167 fn unwrap_std_mut(&mut self) -> &mut Self::StdImpl {
168 match self {
169 #struct_name(#field_type_ident::Std(inner)) => inner,
170 _ => panic!("Expected Std variant"),
171 }
172 }
173
174 #[cfg(feature = #tokio_gated)]
175 fn unwrap_tokio_mut(&mut self) -> &mut Self::TokioImpl {
176 match self {
177 #struct_name(#field_type_ident::Tokio(inner)) => inner,
178 _ => panic!("Expected Tokio variant"),
179 }
180 }
181
182 #[cfg(all(not(feature = #tokio_gated), feature = "tokio"))]
183 fn unwrap_tokio_mut(&mut self) -> &mut Self::TokioImpl {
184 match self {
185 #struct_name(#field_type_ident::Std(inner)) => inner,
186 _ => panic!("Expected Std variant"),
187 }
188 }
189
190 fn get_std(self) -> Option<Self::StdImpl> {
191 match self {
192 #struct_name(#field_type_ident::Std(inner)) => Some(inner),
193 _ => None,
194 }
195 }
196
197 #[cfg(feature = #tokio_gated)]
198 fn get_tokio(self) -> Option<Self::TokioImpl> {
199 match self {
200 #struct_name(#field_type_ident::Tokio(inner)) => Some(inner),
201 _ => None,
202 }
203 }
204
205 #[cfg(all(not(feature = #tokio_gated), feature = "tokio"))]
206 fn get_tokio(self) -> Option<Self::TokioImpl> {
207 match self {
208 #struct_name(#field_type_ident::Std(inner)) => Some(inner),
209 _ => None,
210 }
211 }
212
213 fn get_std_ref(&self) -> Option<&Self::StdImpl > {
214 match self {
215 #struct_name(#field_type_ident::Std(inner)) => Some(inner),
216 _ => None,
217 }
218 }
219
220 #[cfg(feature = #tokio_gated)]
221 fn get_tokio_ref(&self) -> Option<&Self::TokioImpl> {
222 match self {
223 #struct_name(#field_type_ident::Tokio(inner)) => Some(inner),
224 _ => None,
225 }
226 }
227
228 #[cfg(all(not(feature = #tokio_gated), feature = "tokio"))]
229 fn get_tokio_ref(&self) -> Option<&Self::TokioImpl> {
230 match self {
231 #struct_name(#field_type_ident::Std(inner)) => Some(inner),
232 _ => None,
233 }
234 }
235
236 fn get_std_mut(&mut self) -> Option<&mut Self::StdImpl > {
237 match self {
238 #struct_name(#field_type_ident::Std(inner)) => Some(inner),
239 _ => None,
240 }
241 }
242
243 #[cfg(feature = #tokio_gated)]
244 fn get_tokio_mut(&mut self) -> Option<&mut Self::TokioImpl> {
245 match self {
246 #struct_name(#field_type_ident::Tokio(inner)) => Some(inner),
247 _ => None,
248 }
249 }
250
251 #[cfg(all(not(feature = #tokio_gated), feature = "tokio"))]
252 fn get_tokio_mut(&mut self) -> Option<&mut Self::TokioImpl> {
253 match self {
254 #struct_name(#field_type_ident::Std(inner)) => Some(inner),
255 _ => None,
256 }
257 }
258 }
259 };
260 };
261
262 output.into()
263}