1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse_quote, Stmt};
4
5#[proc_macro_attribute]
6pub fn req(attr: TokenStream, item: TokenStream) -> TokenStream {
7 let mut req_ids = mantra_lang_tracing::extract::extract_req_ids(attr.into())
8 .map_err(|err| panic!("{err}"))
9 .unwrap();
10
11 let mut attrbs: Vec<syn::Attribute> = vec![parse_quote!(#[doc = "# Requirements"])];
12
13 for req in &req_ids {
14 let req_literal = syn::LitStr::new(req, proc_macro2::Span::call_site());
15 let attrb: syn::Attribute;
16
17 if let Ok(url) = std::env::var("MANTRA_REQUIREMENT_BASE_URL") {
18 let url_literal = syn::LitStr::new(&url, proc_macro2::Span::call_site());
19 attrb = parse_quote!(#[doc = concat!("- [", #req_literal, "](", #url_literal, #req_literal, ")")]);
20 } else {
21 attrb = parse_quote!(#[doc = concat!("- ", #req_literal)]);
22 }
23 attrbs.push(attrb);
24 }
25
26 if let Ok(parsed_item) = syn::parse::<syn::Item>(item) {
27 match parsed_item {
28 syn::Item::Const(mut const_item) => {
29 const_item.attrs.append(&mut attrbs);
30 quote!(#const_item).into()
31 }
32 syn::Item::Enum(mut enum_item) => {
33 enum_item.attrs.append(&mut attrbs);
34 quote!(#enum_item).into()
35 }
36 syn::Item::ExternCrate(mut extern_item) => {
37 extern_item.attrs.append(&mut attrbs);
38 quote!(#extern_item).into()
39 }
40 syn::Item::Fn(mut fn_item) => {
41 req_ids.reverse();
43
44 for req in req_ids {
45 let req_literal = syn::LitStr::new(&req, proc_macro2::Span::call_site());
46 let macro_stmt: Stmt =
47 parse_quote!(mantra_rust_macros::mr_reqcov!(#req_literal););
48
49 fn_item.block.stmts.insert(0, macro_stmt);
50 }
51
52 fn_item.attrs.append(&mut attrbs);
53
54 quote!(#fn_item).into()
55 }
56 syn::Item::ForeignMod(mut foreign_item) => {
57 foreign_item.attrs.append(&mut attrbs);
58 quote!(#foreign_item).into()
59 }
60 syn::Item::Impl(mut impl_item) => {
61 impl_item.attrs.append(&mut attrbs);
62 quote!(#impl_item).into()
63 }
64 syn::Item::Macro(mut macro_item) => {
65 macro_item.attrs.append(&mut attrbs);
66 quote!(#macro_item).into()
67 }
68 syn::Item::Mod(mut mod_item) => {
69 mod_item.attrs.append(&mut attrbs);
70 quote!(#mod_item).into()
71 }
72 syn::Item::Static(mut static_item) => {
73 static_item.attrs.append(&mut attrbs);
74 quote!(#static_item).into()
75 }
76 syn::Item::Struct(mut struct_item) => {
77 struct_item.attrs.append(&mut attrbs);
78 quote!(#struct_item).into()
79 }
80 syn::Item::Trait(mut trait_item) => {
81 trait_item.attrs.append(&mut attrbs);
82 quote!(#trait_item).into()
83 }
84 syn::Item::TraitAlias(mut talias_item) => {
85 talias_item.attrs.append(&mut attrbs);
86 quote!(#talias_item).into()
87 }
88 syn::Item::Type(mut type_item) => {
89 type_item.attrs.append(&mut attrbs);
90 quote!(#type_item).into()
91 }
92 syn::Item::Union(mut union_item) => {
93 union_item.attrs.append(&mut attrbs);
94 quote!(#union_item).into()
95 }
96 syn::Item::Use(mut use_item) => {
97 use_item.attrs.append(&mut attrbs);
98 quote!(#use_item).into()
99 }
100 syn::Item::Verbatim(token) => {
101 if let Ok(mut trait_item_type) = syn::parse::<syn::TraitItemType>(token.into()) {
102 trait_item_type.attrs.append(&mut attrbs);
103 quote!(#trait_item_type).into()
104 } else {
105 panic!("`req` macro may only be used on Rust items. See: https://doc.rust-lang.org/reference/items.html")
106 }
107 }
108 _ => panic!("`req` macro may only be used on Rust items. See: https://doc.rust-lang.org/reference/items.html"),
109 }
110 } else {
111 panic!("`req` macro may only be used on Rust items. See: https://doc.rust-lang.org/reference/items.html")
112 }
113}
114
115#[proc_macro]
116pub fn reqcov(input: TokenStream) -> TokenStream {
117 let req_ids = mantra_lang_tracing::extract::extract_req_ids(input.into())
118 .map_err(|err| panic!("{err}"))
119 .unwrap();
120
121 let mut stream = TokenStream::new();
122
123 for req in req_ids {
124 let req_literal = syn::LitStr::new(&req, proc_macro2::Span::call_site());
125 stream.extend::<TokenStream>(quote!(mantra_rust_macros::mr_reqcov!(#req_literal);).into())
126 }
127
128 stream
129}