1use chrono::prelude::*;
2use proc_macro::TokenStream;
3use proc_macro2::Span;
4use quote::ToTokens;
5use std::{str::FromStr, sync::atomic::AtomicI64};
6use syn::{
7 parse::{Parse, ParseStream},
8 Block, Ident, LitInt, LitStr, Token, TypePath,
9};
10
11#[proc_macro]
12pub fn compile_time(_: TokenStream) -> TokenStream {
13 let dt = Local::now();
14
15 let current = format!(
16 "{}{:02}{:02}.{:02}{:02}{:02}",
17 dt.year(),
18 dt.month(),
19 dt.day(),
20 dt.hour(),
21 dt.minute(),
22 dt.second()
23 );
24
25 let expanded = quote::quote! {
26 #current
27 };
28 expanded.into()
29}
30
31enum Parser {
32 _Block(Block),
33 _Ident(Ident),
34}
35
36impl Parser {
37 fn parse_ident(input: ParseStream) -> syn::Result<Ident> {
38 let mut symbol = String::new();
39 let ident: Ident = input.parse()?;
40 input.parse::<Token![,]>()?;
41 let n: LitInt = input.parse()?;
42
43 symbol.push_str(ident.to_string().trim_start_matches("r#"));
44 symbol.push('_');
45 symbol.push_str(n.to_string().as_str());
46 Ok(Ident::new(symbol.as_str(), ident.span()))
47 }
48}
49
50impl Parse for Parser {
51 fn parse(input: ParseStream) -> syn::Result<Self> {
52 if !input.peek(Ident) {
53 return Ok(Parser::_Block(input.parse()?));
54 }
55 Ok(Parser::_Ident(Parser::parse_ident(input)?))
56 }
57}
58
59fn id64() -> i64 {
60 static N: AtomicI64 = AtomicI64::new(0);
61 N.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
62}
63
64#[proc_macro]
65pub fn int64(input: TokenStream) -> TokenStream {
66 struct MacroLiteral(i64);
67 impl Parse for MacroLiteral {
68 fn parse(input: ParseStream) -> syn::Result<Self> {
69 if !input.peek(LitInt) {
70 return Ok(MacroLiteral(0));
71 }
72 let n: LitInt = input.parse()?;
73 Ok(MacroLiteral(n.to_string().parse().unwrap()))
74 }
75 }
76 let MacroLiteral(n) = syn::parse_macro_input!(input as MacroLiteral);
77 let mut mask: i64 = -1;
78 if n != 0 {
79 mask = 0xfff << 52;
80 }
81
82 let id = n << 52 | id64() & mask;
83 let expanded = quote::quote! {
84 #id
85 };
86 expanded.into()
87}
88
89#[proc_macro]
90pub fn str64(input: TokenStream) -> TokenStream {
91 struct MacroLiteral(String);
92 impl Parse for MacroLiteral {
93 fn parse(input: ParseStream) -> syn::Result<Self> {
94 if !input.peek(LitStr) {
95 return Ok(MacroLiteral("".to_owned()));
96 }
97 let s: LitStr = input.parse()?;
98 Ok(MacroLiteral(s.value()))
99 }
100 }
101 let MacroLiteral(s) = syn::parse_macro_input!(input as MacroLiteral);
102
103 let id = format!("{}{}", s, id64());
104 let expanded = quote::quote! {
105 #id
106 };
107 expanded.into()
108}
109
110#[proc_macro]
111pub fn _suffix(input: TokenStream) -> TokenStream {
112 let input = syn::parse_macro_input!(input as Parser);
113 match input {
114 Parser::_Block(b) => {
115 let stmts = b.stmts;
116 let seq = LitInt::new(&id64().to_string(), Span::call_site());
117 let expanded = quote::quote! {
118 macro_rules! n {
119 ($iden:ident) => {
120 $crate::_suffix!($iden, #seq)
121 }
122 }
123 #(#stmts)*
124 };
125 expanded.into()
126 }
127
128 Parser::_Ident(i) => {
129 let expanded = quote::quote! {
130 #i
131 };
132 expanded.into()
133 }
134 }
135}
136
137#[proc_macro]
138pub fn make(input: TokenStream) -> TokenStream {
139 fn gen_code(code: &mut String, template: &str, typename: &str, iface: &str) {
140 match iface {
141 "Send" | "Sync" => code.push_str(
142 format!("unsafe impl{} {} for {} {{}}\n", template, iface, typename).as_str(),
143 ),
144 "Default" => code.push_str(
145 format!(
146 "impl{} {} for {} {{ fn default() -> Self {{ Self::new() }} }}\n",
147 template, iface, typename
148 )
149 .as_str(),
150 ),
151 _ => panic!("only Send, Sync, Default is allowed"),
152 };
153 }
154
155 struct MacroLiteral(String);
156 impl Parse for MacroLiteral {
157 fn parse(input: ParseStream) -> syn::Result<Self> {
158 let symbol: TypePath = input.parse()?;
159 input.parse::<Token!(:)>()?;
160
161 let mut typename = symbol.to_token_stream().to_string();
162 typename.retain(|c| !c.is_whitespace());
163 let template = match typename.find('<') {
164 Some(idx) => &typename[idx..],
165 None => "",
166 };
167
168 fn keep() -> impl FnMut(&char) -> bool {
169 let mut level = 0;
170 let mut inner: Option<Box<dyn FnMut(&char) -> bool>> = None;
171 move |c: &char| -> bool {
172 match inner {
173 Some(ref mut fptr) => {
174 if *c == '<' {
175 level += 1;
176 } else if *c == '>' {
177 level -= 1;
178 if level == -1 {
179 inner.take();
180 return true;
181 }
182 }
183 fptr(c)
184 }
185 None => {
186 if level == 0 {
187 if *c == ':' {
188 level = 1;
189 } else if *c == '<' {
190 inner = Some(Box::new(keep()));
191 }
192 } else if *c == ',' || *c == '>' {
193 level -= 1;
194 } else if *c == '<' {
195 level += 1;
196 }
197 level == 0
198 }
199 }
200 }
201 }
202 let typename: String = typename.chars().filter(keep()).collect();
203
204 let mut code = String::new();
205 loop {
206 let symbol: TypePath = input.parse()?;
207 let iface = symbol.path.get_ident().take().unwrap().to_string();
208 gen_code(&mut code, template, &typename, &iface);
209
210 if !input.peek(Token!(,)) {
211 break;
212 }
213 input.parse::<Token!(,)>()?;
214 }
215 Ok(MacroLiteral(code))
216 }
217 }
218
219 let input = syn::parse_macro_input!(input as MacroLiteral);
220 TokenStream::from_str(input.0.as_str()).unwrap()
221}