1use proc_macro::TokenStream;
15use proc_macro2::{Ident, Span};
16use quote::{quote, ToTokens};
17use serde::Deserialize;
18use serde_json::Value;
19use std::io::Read;
20use std::fs;
21use syn::{
22 parse_file, parse_macro_input, parse_quote, punctuated::Punctuated, AttributeArgs, Item,
23 ItemFn, Lit, Meta, NestedMeta, Pat, Stmt, Token,
24};
25
26#[derive(Debug, Deserialize)]
31struct ConfWorkSpace {
32 workspace: Option<Member>,
33 package: Option<Name>,
34}
35
36#[derive(Debug, Deserialize)]
38struct Member {
39 members: Option<Vec<String>>,
40}
41
42#[derive(Debug, Deserialize)]
44struct Name {
45 #[allow(dead_code)]
46 name: String,
47}
48
49#[proc_macro_attribute]
58pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
59 let mut input = parse_macro_input!(item as ItemFn);
60 let attrs = &input.attrs;
61 let vis = &input.vis;
62 let sig = &mut input.sig;
63 let body = &input.block;
64 let _name = &sig.ident;
65
66 if sig.asyncness.is_none() {
67 return syn::Error::new_spanned(sig.fn_token, "仅支持 async fn")
68 .to_compile_error()
69 .into();
70 }
71 sig.asyncness = None;
72
73 (quote! {
74 #(#attrs)*
75 #vis #sig {
76 summer_boot::rt::SummerRuntime::new()
77 .block_on(async move { #body });
78 }
79 })
80 .into()
81}
82
83#[proc_macro_attribute]
97pub fn auto_scan(args: TokenStream, input: TokenStream) -> TokenStream {
98 let mut project = Vec::<String>::new();
99 let mut filter_paths = Vec::<String>::new();
100 let mut cargo_toml = fs::File::open("Cargo.toml").expect("Cargo Toml文件找不到");
102 let mut content = String::new();
103
104 cargo_toml
105 .read_to_string(&mut content)
106 .expect("Cargo内容为空");
107
108 if let Ok(conf_work_space) = toml::from_str::<ConfWorkSpace>(&content) {
110 if let Some(workspace) = conf_work_space.workspace {
111 if let Some(members) = workspace.members {
112 for member in members {
113 project.push(format!("{}/{}", member, "src"));
114 }
115 }
116 } else if project.len() == 0 {
117 if let Some(_) = conf_work_space.package {
118 project.push("src".to_string());
119 }
120 }
121 }
122
123 let args = parse_macro_input!(args as AttributeArgs);
125 for arg in args {
126 if let NestedMeta::Lit(Lit::Str(project)) = arg {
127 filter_paths.push(project.value());
128 }
129 }
130
131 let mut input = parse_macro_input!(input as ItemFn);
133
134 if let Some((master_index, master_name)) = scan_master_fn(&mut input) {
139 let mut listener_addr = String::from("0.0.0.0:");
141 let mut app_context_path = String::from("");
142 let config = summer_boot_autoconfigure::load_conf();
143 if let Some(config) = config {
144 let read_server = serde_json::to_string(&config.server).expect("读取服务配置文件失败");
145 let v: Value = serde_json::from_str(&read_server).expect("读取服务配置文件失败");
146 let port = v["port"].to_string();
147 let context_path = v["context_path"].to_string();
148 listener_addr.push_str(&port);
149 app_context_path.push_str(&context_path);
150 }
151
152 for path in project {
154 scan_method(
155 &path,
156 &filter_paths,
157 &mut input,
158 &app_context_path,
159 (master_index, &master_name),
160 );
161 }
162
163 input.block.stmts.push(parse_quote! {
165 #master_name.listen(#listener_addr).await.expect("配置listen失败");
166 });
167 }
168
169 TokenStream::from(input.into_token_stream())
171}
172
173fn scan_master_fn(input: &mut ItemFn) -> Option<(i32, Ident)> {
178 let mut master_index: i32 = -1;
179 let mut master_name = Ident::new("app", Span::call_site());
180
181 for (index, stmt) in (&mut input.block.stmts).iter_mut().enumerate() {
182 let master = stmt.to_token_stream().to_string();
183 if let Some(_) = master.find("summer_boot :: run()") {
184 master_index = index as i32;
185 }
186 }
187 if master_index < 0 {
188 None
189 } else {
190 if let Stmt::Local(local) = &input.block.stmts[master_index as usize] {
191 let pat = &local.pat;
193
194 if let Pat::Ident(pat_ident) = pat {
195 let name = pat_ident.ident.to_string();
196 master_name = Ident::new(&name, Span::call_site());
197 }
198 } else {
199 input.block.stmts.remove(master_index as usize);
202 input.block.stmts.insert(
203 master_index as usize,
204 parse_quote! {
205 let mut app = summer_boot::run();
206 },
207 )
208 }
209
210 Some((master_index, master_name))
211 }
212}
213
214fn scan_method(
218 path: &str,
219 filter_paths: &Vec<String>,
220 input_token_stream: &mut ItemFn,
221 context_path: &str,
222 (mut master_index, master_name): (i32, &Ident),
223) {
224 if let Ok(entries) = fs::read_dir(path) {
225 for entry in entries {
226 if let Ok(entry) = entry {
227 let file_path = entry.path();
228 if file_path.is_file() {
229 if let Some(extension) = file_path.extension() {
230 if extension == "rs" {
231 if filter_paths.iter().any(|p| path.contains(p)) {
232 return;
233 }
234 let content = fs::read_to_string(entry.path()).expect("处理内部细节");
236 let ast = parse_file(&content).expect("解析文件失败");
238 let items = ast.items;
239 for item in items {
240 if let Item::Fn(item) = item {
241 for attr in item.attrs {
243 if let Meta::List(meta) =
245 attr.parse_meta().expect("所有所有宏信息")
246 {
247 let attr_path = meta.path.to_token_stream().to_string();
249
250 let method = config_req_type(&attr_path);
251 if method.is_none() {
252 continue;
253 }
254 let method =
255 method.expect("是否为指定的宏").to_token_stream();
256
257 let fn_name: &String = &item.sig.ident.to_string();
259 let fn_path_token_stream = config_function_path(
260 &file_path.to_str().unwrap_or("文件为空"),
261 fn_name,
262 );
263
264 let attr_url = meta
266 .nested
267 .into_iter()
268 .next()
269 .expect("summer_boot 的宏信息");
270 if let NestedMeta::Lit(Lit::Str(url)) = attr_url {
271 let url = url.value();
272 let url = format!("{}{}", context_path, url)
273 .replace("\"", "")
274 .replace("//", "/");
275
276 if input_token_stream.block.stmts.len() < 1 {
277 break;
279 } else {
280 master_index += 1;
282 input_token_stream.block.stmts.insert(
283 master_index as usize,
284 parse_quote! {
285 #master_name.at(#url).#method(#fn_path_token_stream);
286 },
287 );
288 }
289 }
290 }
291 }
292 }
293 }
294 }
295 }
296 }
297 }
298 }
299 }
300}
301
302fn config_function_path(path: &str, fu_name: &str) -> proc_macro2::TokenStream {
306 let mut fn_path_idents = Punctuated::<Ident, Token![::]>::new();
307 fn_path_idents.push(Ident::new("crate", Span::call_site()));
308
309 let names: Vec<&str> = path
311 [path.find("src").expect("转换src") + 4..path.rfind(".rs").expect("转换rs后缀")]
312 .split("/")
313 .collect();
314
315 let len = names.len();
316 for (index, name) in names.into_iter().enumerate() {
317 if (index + 1) == len {
318 match name {
320 "main" | "mod" | "lib" => {
321 break;
322 }
323 _ => {}
324 }
325 }
326 if !name.is_empty() {
327 fn_path_idents.push(Ident::new(name, Span::call_site()));
329 }
330 }
331 fn_path_idents.push(Ident::new(fu_name, Span::call_site()));
333
334 fn_path_idents.to_token_stream()
335}
336
337fn config_req_type(attr_path: &str) -> Option<Ident> {
339 if attr_path == "summer_boot_macro :: get"
340 || attr_path == "summer_boot :: get"
341 || attr_path == "get"
342 || attr_path == "summer_boot_macro :: head"
343 || attr_path == "summer_boot :: head"
344 || attr_path == "head"
345 || attr_path == "summer_boot_macro :: put"
346 || attr_path == "summer_boot :: put"
347 || attr_path == "put"
348 || attr_path == "summer_boot_macro :: post"
349 || attr_path == "summer_boot :: post"
350 || attr_path == "post"
351 || attr_path == "summer_boot_macro :: delete"
352 || attr_path == "summer_boot :: delete"
353 || attr_path == "delete"
354 || attr_path == "summer_boot_macro :: head"
355 || attr_path == "summer_boot :: head"
356 || attr_path == "head"
357 || attr_path == "summer_boot_macro :: options"
358 || attr_path == "summer_boot :: options"
359 || attr_path == "options"
360 || attr_path == "summer_boot_macro :: connect"
361 || attr_path == "summer_boot :: connect"
362 || attr_path == "connect"
363 || attr_path == "summer_boot_macro :: patch"
364 || attr_path == "summer_boot :: patch"
365 || attr_path == "patch"
366 || attr_path == "summer_boot_macro :: trace"
367 || attr_path == "summer_boot :: trace"
368 || attr_path == "trace"
369 {
370 if attr_path.starts_with("summer_boot_macro ::") {
371 return Some(Ident::new(
372 &attr_path["summer_boot_macro :: ".len()..],
373 Span::call_site(),
374 ));
375 } else if attr_path.starts_with("summer_boot ::") {
376 return Some(Ident::new(
377 &attr_path["summer_boot :: ".len()..],
378 Span::call_site(),
379 ));
380 } else {
381 return Some(Ident::new(attr_path, Span::call_site()));
382 }
383 } else {
384 return None;
385 }
386}
387
388macro_rules! doc_comment {
389 ($x:expr; $($tt:tt)*) => {
390 #[doc = $x]
391 $($tt)*
392 };
393}
394
395macro_rules! method_macro {
396 (
397 $($method:ident,)+
398 ) => {
399 $(doc_comment! {
400concat!("
401# 功能
402创建路由接口,用于`summer_boot.new()`的返回值使用,
403该函数提供了对应方法`summer_boot/src/web2/gateway/routes.rs`文件下的所有路由方法,
404
405# 支持的路由如下:
406- get
407- head
408- put
409- post
410- delete
411- options
412- connect
413- patch
414- trace
415
416# 例子:
417```rust
418# use summer_boot::{Request, Result};
419#[summer_boot_macro::", stringify!($method), r#"("/")]
420async fn example(mut req: Request<()>) -> Result {
421 Ok(format!("Hello World").into())
422}
423```
424"#);
425 #[proc_macro_attribute]
426 pub fn $method(_args: TokenStream, input: TokenStream) -> TokenStream {
427
428 let mut input = parse_macro_input!(input as ItemFn);
429 let attrs = &input.attrs;
430 let vis = &input.vis;
431 let sig = &mut input.sig;
432 let body = &input.block;
433 let _name = &sig.ident;
434 if sig.asyncness.is_none() {
435 return syn::Error::new_spanned(sig.fn_token, "仅支持 async fn")
436 .to_compile_error()
437 .into();
438 }
439
440 (quote! {
441 #(#attrs)*
442 #vis #sig
443 #body
444 }).into()
445 }
446 })+
447 };
448}
449
450method_macro!(get, head, put, post, delete, patch, trace, options, connect,);