iridis_url_scheme_derive/
lib.rs1extern crate proc_macro;
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::{
9 DeriveInput, ImplItem, ItemImpl, ReturnType, Token,
10 parse::{Parse, ParseStream},
11 parse_macro_input,
12 punctuated::Punctuated,
13};
14
15#[proc_macro_derive(UrlSchemePlugin)]
18pub fn derive_url_scheme_plugin(input: TokenStream) -> TokenStream {
19 let input = parse_macro_input!(input as DeriveInput);
20 let name = input.ident;
21
22 let expanded = quote! {
23 #[cfg(feature = "cdylib")]
24 #[doc(hidden)]
25 #[unsafe(no_mangle)]
26 pub static IRIDIS_URL_SCHEME_PLUGIN: DynamicallyLinkedUrlSchemePluginInstance =
27 || <#name>::new();
28
29 static DEFAULT_TOKIO_RUNTIME: std::sync::LazyLock<iridis_file_ext::prelude::thirdparty::tokio::runtime::Runtime> =
30 std::sync::LazyLock::new(|| {
31 iridis_url_scheme::prelude::thirdparty::tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime")
32 });
33
34 fn default_runtime<T: Send + 'static>(
35 task: impl Future<Output = T> + Send + 'static,
36 ) -> iridis_url_scheme::prelude::thirdparty::tokio::task::JoinHandle<T> {
37 match iridis_url_scheme::prelude::thirdparty::tokio::runtime::Handle::try_current() {
38 Ok(handle) => handle.spawn(task),
39 Err(_) => DEFAULT_TOKIO_RUNTIME.spawn(task),
40 }
41 }
42 };
43
44 TokenStream::from(expanded)
45}
46
47struct MacroArgs {
48 runtime: String,
49}
50
51impl Parse for MacroArgs {
52 fn parse(input: ParseStream) -> syn::Result<Self> {
53 let mut runtime = String::new();
54
55 let vars = Punctuated::<syn::Meta, Token![,]>::parse_terminated(input)?;
56
57 for var in vars {
58 if let syn::Meta::NameValue(name_value) = var {
59 let name = name_value.path.get_ident().unwrap().to_string();
60
61 if name == "runtime" {
62 if let syn::Expr::Lit(lit) = &name_value.value {
63 if let syn::Lit::Str(lit_str) = &lit.lit {
64 runtime = lit_str.value();
65 }
66 }
67 }
68 }
69 }
70
71 Ok(MacroArgs { runtime })
72 }
73}
74
75#[proc_macro_attribute]
96pub fn url_scheme_plugin(attr: TokenStream, item: TokenStream) -> TokenStream {
97 let mut impl_block = parse_macro_input!(item as ItemImpl);
98
99 let args = parse_macro_input!(attr as MacroArgs);
100 let runtime_tokens = args.runtime.parse::<proc_macro2::TokenStream>().unwrap();
101
102 for item in &mut impl_block.items {
103 if let ImplItem::Fn(method) = item {
104 let was_async = method.sig.asyncness.is_some();
105 method.sig.asyncness = None;
106
107 let old_block = method.block.clone();
108
109 if was_async {
110 let old_return_type = match &method.sig.output {
111 ReturnType::Default => quote! { () },
112 ReturnType::Type(_, ty) => {
113 if method.sig.ident == "new" {
114 quote! { iridis_url_scheme::prelude::thirdparty::eyre::Result<Box<dyn iridis_url_scheme::prelude::UrlSchemePlugin>> }
115 } else {
116 quote! { #ty }
117 }
118 }
119 };
120
121 method.sig.output = syn::parse_quote! {
122 -> iridis_url_scheme::prelude::thirdparty::tokio::task::JoinHandle<#old_return_type>
123 };
124
125 if method.sig.ident == "new" {
126 method.block = syn::parse_quote! {
127 {
128 #runtime_tokens(async move {
129 #old_block.map(|node| Box::new(node) as Box<dyn iridis_url_scheme::prelude::UrlSchemePlugin>)
130 })
131 }
132 };
133 } else if method.sig.ident == "load" {
134 method.block = syn::parse_quote! {
135 {
136 #runtime_tokens(async move {
137 #old_block
138 })
139 }
140 };
141 }
142 }
143 }
144 }
145
146 quote! {
147 #impl_block
148 }
149 .into()
150}