1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
//! 运行时宏处理
//!
//! # main
//! 使用运行时宏来设置summerboot async运行时。参见[main]宏文档。
//!
//! # auto_scan
//! 提供了基础的`auto_scan`功能用于发现并自动注册路由。
//!
//! # post、get、delete、put、patch、head、options、connect、trace
//! 提供了简单的路由宏标注。
//!
//!

use proc_macro::{TokenStream};
use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens};
use serde::Deserialize;
use std::fs;
use std::io::Read;
use syn::{
    parse_file, parse_macro_input, parse_quote, punctuated::Punctuated, Item, ItemFn, Lit, Meta,
    NestedMeta, Token, Stmt, Pat, Block,
};
use serde_json::{Value};

/// 用于匹配项目根目录下的 `Cargo.toml` 文件。
/// 匹配规则为:
/// 1. workspace下的member的数组格式
/// 2. 在package下的name字段
#[derive(Debug, Deserialize)]
struct ConfWorkSpace {
    workspace: Option<Member>,
    package: Option<Name>,
}

/// 匹配workspace下的member数组格式
#[derive(Debug, Deserialize)]
struct Member {
    members: Option<Vec<String>>,
}

/// 匹配package下的name字段
#[derive(Debug, Deserialize)]
struct Name {
    name: String,
}

/// 用于标记 summer_boot web 的入口点
/// # Examples
/// ```
/// #[summer_boot::main]
/// async fn main() {
///     async { println!("Hello world"); }.await
/// }
/// ```
#[proc_macro_attribute]
pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
    let mut input = parse_macro_input!(item as ItemFn);
    let attrs = &input.attrs;
    let vis = &input.vis;
    let sig = &mut input.sig;
    let body = &input.block;
    let _name = &sig.ident;

    if sig.asyncness.is_none() {
        return syn::Error::new_spanned(sig.fn_token, "仅支持 async fn")
            .to_compile_error()
            .into();
    }
    sig.asyncness = None;

    (quote! {
        #(#attrs)*
        #vis #sig {
            summer_boot::rt::SummerRuntime::new()
            .block_on(async move { #body });
        }
    })
    .into()
}

/// 完成 summer_boot 项目下的自动扫描功能,会先扫描找到`summer_boot::run();`
/// 函数,然后在此处进行装配活动。
///
/// 注意:如果需要在此处添加运行时,必须在当前宏的后面配置,否则无法完成装配
/// # Examples
/// ```rust
/// // #[summer_boot::auto_scan]
/// fn main() {
///     summer_boot::run();
/// }
/// ```
#[proc_macro_attribute]
pub fn auto_scan(_: TokenStream, input: TokenStream) -> TokenStream {
    let mut project = Vec::<String>::new();

    // 找到需要扫描的路径
    let mut cargo_toml = fs::File::open("Cargo.toml").unwrap();
    let mut content = String::new();
    cargo_toml.read_to_string(&mut content).unwrap();

    // 根据包类型分别处理
    if let Ok(conf_work_space) = toml::from_str::<ConfWorkSpace>(&content) {
        if let Some(workspace) = conf_work_space.workspace {
            if let Some(members) = workspace.members {
                for member in members {
                    project.push(format!("{}/{}", member, "src"));
                }
            }
        } else if project.len() == 0 {
            if let Some(_) = conf_work_space.package {
                project.push("src".to_string());
            }
        }
    }

    let mut input = parse_macro_input!(input as ItemFn);
    
    // 查找主函数的位置和是否存在变量名
    // 未找到则直接退出宏处理
    // 变量名不存在则默认添加app
    // 如果存在则会返回出来,供后续使用
    if let Some((master_index, master_name)) = scan_master_fn(&mut input) {

        // 开始扫描
        for path in project {
            scan_method(&path, &mut input, (master_index, &master_name));
        }

        // 解析yaml文件
        let mut listener_addr = String::from("0.0.0.0:");
        let config = summer_boot_autoconfigure::load_conf();
        if let Some(config) = config {
            let read_server = serde_json::to_string(&config.server).unwrap();
            let v: Value = serde_json::from_str(&read_server).unwrap();
            let port = v["port"].to_string();
            listener_addr.push_str(&port);
        }

        // 配置listen
        input.block.stmts.push(parse_quote!{
            #master_name.listen(#listener_addr).await.unwrap();
        });
    }

    // 构建新的函数结构,增加函数行
    TokenStream::from(input.into_token_stream())
}

// 扫描函数,找到主函数
// 返回主函数所在的位置索引,并判断是否存在变量名
// 如果存在,则找到并返回
// 如果不存在,则删除默认主函数,添加新的主函数
fn scan_master_fn(input: &mut ItemFn) -> Option<(i32, Ident)> {
    let mut master_index: i32 = -1;
    let mut master_name = Ident::new("app", Span::call_site());

    for (index, stmt) in (&mut input.block.stmts).iter_mut().enumerate() {
        let master = stmt.to_token_stream().to_string();
        if let Some(_) = master.find("summer_boot :: run()") {
            master_index = index as i32;
        }
    }
    if master_index < 0 {
        None
    } else {
        if let Stmt::Local(local) = &input.block.stmts[master_index as usize] {
            // 函数存在变量,需要获取变量名称
            let pat = &local.pat;

            let x: TokenStream = (quote! {
                #pat
            }).into();
            println!("data:{}", x);
            if let Pat::Ident(patIdent) = pat {
                let name = patIdent.ident.to_string();
                master_name = Ident::new(&name, Span::call_site());
            }
        } else {
            // 函数不存在变量,需要手动添加
            // TODO 目前相对简单,删除当前函数,并添加指定的函数即可,后续建议修改
            input.block.stmts.remove(master_index as usize);
            input.block.stmts.insert(master_index as usize, parse_quote!{
                let mut app = summer_boot::run();
            })
        }


        Some((master_index, master_name))
    }
}

// 判断是否是目录,如果是路径则需要循环递归处理,
// 如果是文件则直接处理
// 处理过程中会将函数调用函数拼接,然后插入到指定的位置 下标+1 的位置
fn scan_method(path: &str, input: &mut ItemFn, (mut master_index, master_name): (i32, &Ident)) {
    let mut file = fs::File::open(path).unwrap();
    let file_type = file.metadata().unwrap();
    if file_type.is_dir() {
        // 获取当前文件夹下的所有文件
        let mut files = fs::read_dir(path).unwrap();

        // 循环里面的所有文件
        while let Some(file) = files.next() {
            let file = file.unwrap();
            // TODO 过滤带test文件夹的扫描
            scan_method(&file.path().to_str().unwrap(), input, (master_index, master_name));
        }
    } else {
        // 判断文件名后缀是否是.rs
        if path.ends_with(".rs") {
            // 如果是文件,则处理内部细节
            let mut content = String::new();
            file.read_to_string(&mut content).unwrap();

            // 解析文件
            let ast = parse_file(&content).unwrap();
            let items = ast.items;
            for item in items {
                if let Item::Fn(item) = item {
                    // 处理函数中的函数名,指定宏信息
                    for attr in item.attrs {
                        // 遍历所有宏信息
                        if let Meta::List(meta) = attr.parse_meta().unwrap() {
                            // 判断宏是否为指定的宏
                            let attr_path = meta.path.to_token_stream().to_string();

                            let method = config_req_type(&attr_path);
                            if method.is_none() {
                                continue;
                            }
                            let method = method.unwrap().to_token_stream();

                            // 获取函数全路径名
                            let fn_name = &item.sig.ident.to_string();
                            let fn_path_token_stream = config_function_path(&path, fn_name);

                            // 如果是 summer_boot 的宏信息,则处理
                            let attr_url = meta.nested.into_iter().next().unwrap();
                            if let NestedMeta::Lit(Lit::Str(url)) = attr_url {
                                let url = url.value();
                                if input.block.stmts.len() < 1 {
                                    // 如果注入的方法中没有任何代码,则不操作
                                    break;
                                } else {
                                    // 添加,注意下标加 1
                                    master_index += 1;
                                    input.block.stmts.insert(
                                        master_index as usize,
                                        parse_quote! {
                                            #master_name.at(#url).#method(#fn_path_token_stream);
                                        },
                                    );
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

// 配置函数全路径
// 根据相对项目的绝对路径找到函数调用的全路径链
// 注意:目前无法完成文件中mod下的函数调用,无法找到
fn config_function_path(path: &str, fu_name: &str) -> proc_macro2::TokenStream {
    let mut fn_path_idents = Punctuated::<Ident, Token![::]>::new();
    fn_path_idents.push(Ident::new("crate", Span::call_site()));

    // 配置函数路径
    let names: Vec<&str> = (&path[path.find("src/").unwrap() + 4..path.rfind(".rs").unwrap()])
        .split("/")
        .collect();

    let len = names.len();
    for (index, name) in names.into_iter().enumerate() {
        if (index + 1) == len {
            // 最后一个文件名称如果是main、lib、test则不需要加入路径
            match name {
                "main" | "mod" | "lib" => {
                    break;
                }
                _ => {}
            }
        }
        if !name.is_empty() {
            // 配置文件包名
            fn_path_idents.push(Ident::new(name, Span::call_site()));
        }
    }
    // 配置函数名称
    fn_path_idents.push(Ident::new(fu_name, Span::call_site()));

    fn_path_idents.to_token_stream()
}

// 配置请求类型
fn config_req_type(attr_path: &str) -> Option<Ident> {
    if attr_path == "summer_boot_macro :: get"
        || attr_path == "summer_boot :: get"
        || attr_path == "get"
        || attr_path == "summer_boot_macro :: head"
        || attr_path == "summer_boot :: head"
        || attr_path == "head"
        || attr_path == "summer_boot_macro :: put"
        || attr_path == "summer_boot :: put"
        || attr_path == "put"
        || attr_path == "summer_boot_macro :: post"
        || attr_path == "summer_boot :: post"
        || attr_path == "post"
        || attr_path == "summer_boot_macro :: delete"
        || attr_path == "summer_boot :: delete"
        || attr_path == "delete"
        || attr_path == "summer_boot_macro :: head"
        || attr_path == "summer_boot :: head"
        || attr_path == "head"
        || attr_path == "summer_boot_macro :: options"
        || attr_path == "summer_boot :: options"
        || attr_path == "options"
        || attr_path == "summer_boot_macro :: connect"
        || attr_path == "summer_boot :: connect"
        || attr_path == "connect"
        || attr_path == "summer_boot_macro :: patch"
        || attr_path == "summer_boot :: patch"
        || attr_path == "patch"
        || attr_path == "summer_boot_macro :: trace"
        || attr_path == "summer_boot :: trace"
        || attr_path == "trace"
    {
        if attr_path.starts_with("summer_boot_macro ::") {
            return Some(Ident::new(
                &attr_path["summer_boot_macro :: ".len()..],
                Span::call_site(),
            ));
        } else if attr_path.starts_with("summer_boot ::") {
            return Some(Ident::new(
                &attr_path["summer_boot :: ".len()..],
                Span::call_site(),
            ));
        } else {
            return Some(Ident::new(attr_path, Span::call_site()));
        }
    } else {
        return None;
    }
}

macro_rules! doc_comment {
    ($x:expr; $($tt:tt)*) => {
        #[doc = $x]
        $($tt)*
    };
}

macro_rules! method_macro {
    (
        $($method:ident,)+
    ) => {
        $(doc_comment! {
concat!("
# 功能
创建路由接口,用于`summer_boot.new()`的返回值使用,
该函数提供了对应方法`summer_boot/src/web2/gateway/routes.rs`文件下的所有路由方法,

# 支持的路由如下:
- get
- head
- put
- post
- delete
- options
- connect
- patch
- trace

# 例子:
```rust
# use summer_boot::{Request, Result};
#[summer_boot_macro::", stringify!($method), r#"("/")]
async fn example(mut req: Request<()>) -> Result {
    Ok(format!("Hello World").into())
}
```
"#);
            #[proc_macro_attribute]
            pub fn $method(args: TokenStream, input: TokenStream) -> TokenStream {

                let mut input = parse_macro_input!(input as ItemFn);
                let attrs = &input.attrs;
                let vis = &input.vis;
                let sig = &mut input.sig;
                let body = &input.block;
                let _name = &sig.ident;
                if sig.asyncness.is_none() {
                    return syn::Error::new_spanned(sig.fn_token, "仅支持 async fn")
                        .to_compile_error()
                        .into();
                }

                (quote! {
                    #(#attrs)*
                    #vis #sig
                        #body
                }).into()
            }
        })+
    };
}

method_macro!(get, head, put, post, delete, patch, trace, options, connect,);