crossbus_derive/
lib.rs

1extern crate proc_macro;
2
3use core::{ops::ControlFlow, str::FromStr};
4use proc_macro::{TokenStream, TokenTree};
5
6#[proc_macro_derive(Message)]
7pub fn message(stream: TokenStream) -> TokenStream {
8    let mut name = "".to_string();
9    let nonident = ["enum".to_string(), "struct".to_string()];
10    stream
11        .into_iter()
12        // take the first ident as the
13        // name of the struct
14        // then stop iterating
15        .try_for_each(|tree| {
16            if let TokenTree::Ident(ident) = tree {
17                name = ident.to_string();
18                if !nonident.contains(&&name) {
19                    return ControlFlow::Break(());
20                }
21            }
22            ControlFlow::Continue(())
23        });
24    assert_ne!(name, "", "struct/enum name NOT FOUND");
25    let raw_token =
26        r#"impl crossbus::message::Message for <+ident+> { }"#.replace("<+ident+>", &name);
27    TokenStream::from_str(&raw_token).unwrap()
28}
29
30#[proc_macro_attribute]
31pub fn main(attr: TokenStream, stream: TokenStream) -> TokenStream {
32    let mut name = "".to_string();
33    let raw_stream = stream.to_string();
34    if !raw_stream.contains("async fn ") {
35        panic!("attribute for async function ONLY");
36    }
37    let raw_attr = attr.to_string();
38    let rt_vec: Vec<&str> = raw_attr.split(",").filter(|en| *en != "").collect();
39    if rt_vec.len() != 1 {
40        panic!("ONLY one type runtime is allowed");
41    }
42    let rt_str_ = rt_vec[0].replace(" ", "");
43    let rt_str = rt_str_.strip_prefix("runtime=");
44    if rt_str.is_none() {
45        panic!("Runtime not found, HELP: #[main(runtime = \"xx::xx\")]");
46    }
47    let rt_str = rt_str.unwrap();
48    let nonident = [
49        "fn".to_owned(),
50        "async".to_owned(),
51        "pub".to_owned(),
52        "mod".to_owned(),
53        "in".to_owned(),
54        "super".to_owned(),
55        "crate".to_owned(),
56    ];
57    stream
58        .into_iter()
59        // take the first ident as the
60        // name of the struct
61        // then stop iterating
62        .try_for_each(|tree| {
63            if let TokenTree::Ident(ident) = tree {
64                name = ident.to_string();
65                if !nonident.contains(&&name) {
66                    return ControlFlow::Break(());
67                }
68            }
69            ControlFlow::Continue(())
70        });
71    assert_eq!(name, "main", "attribute for main function ONLY");
72    let runtime;
73    match rt_str {
74        "async-std" => {
75            runtime = "crossbus::rt::runtime_async_std::Runtime::block_on";
76        }
77        "tokio" => {
78            runtime = "crossbus::rt::runtime_tokio::Runtime::block_on";
79        }
80        "wasm32" | "wasm64" => {
81            runtime = "crossbus::rt::runtime_wasm32::Runtime::spawn_local";
82        }
83        _ => {
84            runtime = rt_str;
85        }
86    }
87    let raw_token = r#"fn main() {
88<+fn+>
89
90let _ = <+runtime+>(async {
91
92    main().await;
93    crossbus::reactor::Reactor::as_future().await;
94}); }"#
95        .replace("<+fn+>", &raw_stream)
96        .replace("<+runtime+>", runtime);
97    TokenStream::from_str(&raw_token).unwrap()
98}