1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
extern crate proc_macro;

use convert_case::Case;
use convert_case::Casing;
use proc_macro::TokenStream;
use quote::format_ident;
use quote::quote;
use syn::parse::Parse;
use syn::parse::ParseStream;
use syn::parse_macro_input;
use syn::Ident;
use syn::LitStr;
use syn::Token;

struct PluginInfo {
    name: Ident,
    id: LitStr,
}

impl Parse for PluginInfo {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let name: Ident = input.parse()?;
        input.parse::<Token![,]>()?;
        let id: LitStr = input.parse()?;
        Ok(PluginInfo { name, id })
    }
}

#[proc_macro]
pub fn make_plugin(input: TokenStream) -> TokenStream {
    let PluginInfo { name, id } = parse_macro_input!(input as PluginInfo);
    let plugin_name = name.clone();
    let plugin_snake_case_name = plugin_name.to_string().to_case(Case::Snake);

    let parse_fn_name = format_ident!("parse_{plugin_snake_case_name}_message");
    let attach_fn_name = format_ident!("attach_{plugin_snake_case_name}");

    let expanded = quote! {
        use jarust::prelude::*;

        #[async_trait::async_trait]
        pub trait #name: Attach {
            type Event: Send + Sync + 'static;
            type Handle: From<JaHandle> + std::ops::Deref<Target = JaHandle> + PluginTask;

            fn #parse_fn_name(message: JaResponse) -> JaResult<Self::Event>;

            async fn #attach_fn_name(
                &self,
            ) -> JaResult<(Self::Handle, tokio::sync::mpsc::UnboundedReceiver<Self::Event>)> {
                let (handle, mut receiver) = self.attach(#id).await?;
                let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
                let abort_handle = jatask::spawn(async move {
                    while let Some(msg) = receiver.recv().await {
                        let msg = Self::#parse_fn_name(msg)?;
                        let _ = tx.send(msg);
                    }
                    Ok::<(), JaError>(())
                });
                let mut handle: Self::Handle = handle.into();
                handle.assign_aborts(vec![abort_handle]);
                Ok((handle, rx))
            }
        }
    };

    TokenStream::from(expanded)
}