Skip to main content

maybe_fut_unwrap_derive/
lib.rs

1#![crate_name = "maybe_fut_unwrap_derive"]
2#![crate_type = "lib"]
3
4//! # maybe-fut-unwrap-derive
5//!
6//! A procedural macro which exposes the `unwrap` method for `MaybeFuture` types.
7//!
8//! ## Example
9//!
10//! ```rust,ignore
11//! #[derive(Unwrap)]
12//! #[unwrap_types(std(std::fs::File), tokio(tokio::fs::File))]
13//! struct MyWrapper(InnerWrapper);
14//!
15//! enum InnerWrapper {
16//!    Std(std::fs::File),
17//!    Tokio(tokio::fs::File),
18//! }
19//! ```
20
21#![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    // struct must be a tuple struct
39    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    // should be a single field
49    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    // this field must be an Enum
55    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                    // This is the main attribute, we can ignore it
94                    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}