1mod metadata;
2mod package;
3
4use std::{sync::RwLock, collections::HashSet};
5
6use litrs::StringLit;
7use metadata::get_metadata;
8use once_cell::sync::OnceCell;
9use proc_macro::TokenStream;
10use syn::Token;
11
12use crate::metadata::extract_active_pkg;
13
14static STATIC_ACTIVE_PKGS: OnceCell<RwLock<HashSet<String>>> = OnceCell::new();
16
17fn get_active_packages() -> &'static RwLock<HashSet<String>> {
18 let cache = STATIC_ACTIVE_PKGS.get_or_init(|| {
19 let metadata = get_metadata();
21 let active_pkgs = extract_active_pkg(&metadata);
22
23 RwLock::new(active_pkgs)
24 });
25
26 cache
27}
28
29#[derive(Debug)]
30struct Tiaojian {
31 packages: Vec<String>,
32 remove_macro: bool,
33}
34
35#[derive(Debug)]
36struct TiaojianClosure {
37 packages: Vec<String>,
38 body: proc_macro2::TokenStream,
39}
40
41#[derive(Debug)]
42struct RemoveMacro {
43 rest: TokenStream
44}
45
46#[proc_macro_attribute]
47pub fn tiaojian_a(attr: TokenStream, item: TokenStream) -> TokenStream {
48
49 match syn::parse::<Tiaojian>(attr) {
54 Err(e) => {
55 e.into_compile_error().into()
56 },
57 Ok(tj) => {
58 let active_pkgs = get_active_packages();
60 let active_pkgs = active_pkgs.read().unwrap();
61
62 let all_active = tj.packages.iter().all(|pkg|
63 active_pkgs.iter().any(|act_pkg| act_pkg == pkg)
64 );
65
66 if all_active {
67 item
68 } else {
69 if tj.remove_macro {
70 let tmp = syn::parse::<RemoveMacro>(item).unwrap();
71 tmp.rest
72 } else {
73 TokenStream::new()
74 }
75 }
76 },
77 }
78}
79
80#[proc_macro]
90pub fn tiaojian(input: TokenStream) -> TokenStream {
91 match syn::parse::<TiaojianClosure>(input) {
92 Err(e) => {
93 e.into_compile_error().into()
94 },
95 Ok(tj) => {
96 let active_pkgs = get_active_packages();
98 let active_pkgs = active_pkgs.read().unwrap();
99
100 let all_active = tj.packages.iter().all(|pkg|
101 active_pkgs.iter().any(|act_pkg| act_pkg == pkg)
102 );
103
104 if all_active {
105 tj.body.into()
106 } else {
107 TokenStream::new()
108 }
109 },
110 }
111}
112
113impl syn::parse::Parse for TiaojianClosure {
114 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
115 input.parse::<Token![|]>()?;
116
117 let pkg_input_iter = input.cursor().token_stream().into_iter();
118 let mut packages = vec![];
119 for item in pkg_input_iter {
120 match item {
121 proc_macro2::TokenTree::Literal(val) => {
122 let val = StringLit::try_from(val).unwrap();
123 packages.push(val.value().to_owned());
124 input.parse::<syn::LitStr>()?;
125 },
126 proc_macro2::TokenTree::Punct(p) => {
127 if p.to_string() =="|" {
128 break;
129 } else if p.to_string() =="," {
130 input.parse::<Token![,]>()?;
131 }
132 },
133 _ => Err(syn::Error::new(input.span(), "tiaojian macro not support the token".to_owned()))?,
134 }
135 }
136
137 input.parse::<Token![|]>()?;
138 let packages_input;
139 syn::braced!(packages_input in input);
140 let body = packages_input.parse::<proc_macro2::TokenStream>().unwrap();
141 Ok(
142 TiaojianClosure {
143 packages,
144 body,
145 }
146 )
147 }
148}
149
150impl syn::parse::Parse for RemoveMacro {
151 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
152 match input.parse::<Token![#]>() {
153 Ok(_) => {
154 let body_buf;
155 syn::bracketed!(body_buf in input);
156 body_buf.parse::<proc_macro2::TokenStream>().unwrap();
157 Ok(
158 Self {
159 rest: input.parse::<proc_macro2::TokenStream>().unwrap().into()
160 }
161 )
162 },
163 Err(_) => Ok(
164 Self {
165 rest: input.parse::<proc_macro2::TokenStream>().unwrap().into()
166 }
167 ),
168 }
169 }
170}
171
172impl syn::parse::Parse for Tiaojian {
174 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
175 let mut remove_macro = false;
176 let fn_name: syn::Ident = input.parse()?;
177 if fn_name != "is_active" {
178 return Err(syn::Error::new(input.span(), "tiaojian macro is only support build-in fn 'is_actived' now".to_owned()));
179 }
180
181 let packages_input;
182 syn::parenthesized!(packages_input in input);
183 let pkg_input_iter = packages_input.cursor().token_stream().into_iter();
184 let mut packages = vec![];
185
186 for item in pkg_input_iter {
187 match item {
188 proc_macro2::TokenTree::Group(_) => {
189 return Err(syn::Error::new(input.span(), "error not support group in expression".to_owned()))
190 },
191 proc_macro2::TokenTree::Ident(_) => {
192 return Err(syn::Error::new(input.span(), "error not support ident in expression".to_owned()))
193 },
194 proc_macro2::TokenTree::Punct(v) => {
195 if v.to_string() == "," {
196 packages_input.parse::<Token![,]>()?;
197 } else {
198 return Err(syn::Error::new(input.span(), "error only support ',' in expression".to_owned()));
199 }
200 },
201 proc_macro2::TokenTree::Literal(val) => {
202 let val = StringLit::try_from(val).unwrap();
203 packages.push(val.value().to_owned());
204 packages_input.parse::<syn::LitStr>()?;
205 },
206 }
207 }
208
209 match input.parse::<Token![,]>() {
210 Ok(_) => match input.parse::<syn::Ident>() {
211 Ok(val) => {
212 if val == "remove_macro" {
213 remove_macro = true;
214 }
215 },
216 Err(_) =>
217 return Err(syn::Error::new(input.span(), "error need a ident".to_owned())),
218 },
219 Err(_) => {}
220 }
221
222 Ok(
223 Tiaojian {
224 packages,
225 remove_macro
226 }
227 )
228 }
229}