dingtalk_stream_sdk_rust_macro/
lib.rs1use proc_macro2::{Ident, Span, TokenStream, TokenTree};
2use quote::quote;
3use syn::Error;
4
5#[proc_macro]
24pub fn action_card(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
25 try_expand(input, parse)
26}
27
28fn try_expand<P>(input: proc_macro::TokenStream, proc: P) -> proc_macro::TokenStream
29where
30 P: FnOnce(TokenStream) -> Result<TokenStream, Error>,
31{
32 match proc(TokenStream::from(input)) {
33 Ok(tokens) => tokens,
34 Err(err) => err.to_compile_error(),
35 }
36 .into()
37}
38
39fn parse(input: TokenStream) -> Result<TokenStream, Error> {
40 let mut input = input.into_iter().peekable();
41 let title = next(&mut input)?;
42 let text = next(&mut input)?;
43
44 let mut count = 0;
45 let mut actions_expanded = Vec::new();
46 let actions = next(&mut input)?;
47 let TokenTree::Group(group) = actions
48 .into_iter()
49 .next()
50 .ok_or_else(|| Error::new(Span::call_site(), "actions is empty"))?
51 else {
52 return Err(Error::new(Span::call_site(), "actions should inside []"));
53 };
54
55 for item in group.stream() {
56 match item {
57 TokenTree::Ident(..) | TokenTree::Group(..) => {
58 count += 1;
59 let title_key = Ident::new(&format!("action_title_{count}"), Span::call_site());
60 let url_key = Ident::new(&format!("action_url_{count}"), Span::call_site());
61
62 if let TokenTree::Ident(i) = item {
63 actions_expanded.push(quote! {
64 #title_key: #i.0.to_owned(),
65 #url_key: #i.1.to_owned(),
66 });
67 } else if let TokenTree::Group(g) = item {
68 let tokens = g.stream();
69 actions_expanded.push(quote! {
70 #title_key: #tokens.0.to_owned(),
71 #url_key: #tokens.1.to_owned(),
72 });
73 }
74 }
75 _ => {}
76 }
77 }
78
79 let enum_name = Ident::new(&format!("SampleActionCard{count}"), Span::call_site());
80 let quote = quote! {
81 MessageTemplate::#enum_name {
82 title: #(#title)*.to_owned(),
83 text: #(#text)*.to_owned(),
84 #(#actions_expanded)*
85 }
86 };
87
88 Ok(quote)
89}
90
91fn next<I: Iterator<Item = TokenTree>>(i: &mut I) -> Result<Vec<TokenTree>, Error> {
92 let mut result = vec![];
93 loop {
94 let Some(n) = i.next() else {
95 break;
96 };
97
98 if let TokenTree::Punct(ref p) = n {
99 if p.as_char() == ',' {
100 break;
101 }
102 }
103
104 result.push(n);
105 }
106
107 Ok(result)
108}