mantra_rust_procm/
lib.rs

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                // reversed, because statements are inserted at block start
42                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}