Skip to main content

tmr_cargo_macros/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4    parse::{Parse, ParseStream},
5    parse_macro_input, DeriveInput, Ident, ItemImpl,
6};
7use tracing::{info_span, trace};
8
9mod item;
10
11fn tracing_setup() {
12    if tracing::dispatcher::has_been_set() {
13        return;
14    }
15    tracing_subscriber::fmt::SubscriberBuilder::default()
16        .with_env_filter("tmr_macros=trace")
17        .with_span_events(
18            tracing_subscriber::fmt::format::FmtSpan::ENTER
19                | tracing_subscriber::fmt::format::FmtSpan::EXIT,
20        )
21        .pretty()
22        .init();
23}
24
25fn println_fn_ident() -> Ident {
26    Ident::new("println_fn", proc_macro::Span::call_site().into())
27}
28
29fn println_ident() -> Ident {
30    Ident::new("println", proc_macro::Span::call_site().into())
31}
32
33// proc macro attributes
34#[proc_macro_attribute]
35pub fn item(attr: TokenStream, item: TokenStream) -> TokenStream {
36    tracing_setup();
37    let _enter = info_span!("item").entered();
38    let attr = parse_macro_input!(attr as AttributeItem);
39    let impl_definition = parse_macro_input!(item as ItemImpl);
40    let result = item::item(&attr.value.value(), &impl_definition);
41    result.into()
42}
43
44struct AttributeItem {
45    value: syn::LitStr,
46}
47
48impl Parse for AttributeItem {
49    fn parse(input: ParseStream) -> syn::Result<Self> {
50        let value = input.parse()?;
51        Ok(AttributeItem { value })
52    }
53}
54
55#[proc_macro_derive(Item, attributes(key, value, values, route))]
56pub fn derive_package(input: TokenStream) -> TokenStream {
57    tracing_setup();
58    let _enter = info_span!("derive_iteme").entered();
59    let input = parse_macro_input!(input as DeriveInput);
60    let key = input
61        .attrs
62        .iter()
63        .find(|attr| attr.path().is_ident("key"))
64        .unwrap()
65        .parse_args::<syn::LitStr>()
66        .unwrap();
67    let result = item::derive_item(&key.value(), &input);
68    result.into()
69}
70
71struct ItemsCargoToml(Vec<ItemCargoToml>);
72
73impl IntoIterator for ItemsCargoToml {
74    type Item = ItemCargoToml;
75    type IntoIter = std::vec::IntoIter<ItemCargoToml>;
76
77    fn into_iter(self) -> Self::IntoIter {
78        self.0.into_iter()
79    }
80}
81
82impl Parse for ItemsCargoToml {
83    fn parse(input: ParseStream) -> syn::Result<Self> {
84        let mut items = Vec::new();
85        while !input.is_empty() {
86            items.push(input.parse()?);
87        }
88        Ok(ItemsCargoToml(items))
89    }
90}
91
92struct ItemCargoToml(Ident);
93
94impl Parse for ItemCargoToml {
95    fn parse(input: ParseStream) -> syn::Result<Self> {
96        let struct_name = input.parse()?;
97        let _: Option<syn::Token![,]> = input.parse()?;
98        Ok(ItemCargoToml(struct_name))
99    }
100}
101
102impl ItemCargoToml {
103    fn name(&self) -> Ident {
104        self.0.clone()
105    }
106}
107
108#[proc_macro]
109pub fn cargo_toml(input: TokenStream) -> TokenStream {
110    tracing_setup();
111    let _enter = info_span!("cargo_toml").entered();
112    let items = parse_macro_input!(input as ItemsCargoToml);
113    let cargo_toml_contents_ident =
114        Ident::new("cargo_toml_contents", proc_macro::Span::call_site().into());
115    let cargo_toml_build_string_steps = items
116        .into_iter()
117        .map(|item| {
118            let struct_name = item.name();
119            let struct_bound = Ident::new(
120                stringify!(name).to_owned().to_lowercase().as_ref(),
121                proc_macro::Span::call_site().into(),
122            );
123            let result = quote!(
124                let #struct_bound = #struct_name::new();
125                #cargo_toml_contents_ident.push_str(&format!("{}", #struct_bound.println()));
126                #cargo_toml_contents_ident.push_str(&format!("{}", #struct_bound.println_fn()));
127            );
128            trace!("cargo_toml_build_string_step:\n{:#?}", result.to_string());
129            result
130        })
131        .collect::<Vec<_>>();
132    trace!(
133        "cargo_toml_build_string_steps:\n{:#?}",
134        quote!(#(#cargo_toml_build_string_steps)*).to_string()
135    );
136    let cargo_toml_file_ident = Ident::new("cargo_toml_file", proc_macro::Span::call_site().into());
137    let cargo_toml_doc_ident = Ident::new("doc", proc_macro::Span::call_site().into());
138    quote!(
139        fn main() {
140            let mut #cargo_toml_file_ident = std::fs::File::create(
141                std::path::PathBuf::from(env!("PWD")).join(std::path::Path::new(file!()).parent().unwrap().to_path_buf().join("Cargo.toml"))
142            ).unwrap();
143            let #cargo_toml_contents_ident = {
144                let mut #cargo_toml_contents_ident = String::new();
145                #(#cargo_toml_build_string_steps)*
146                let mut #cargo_toml_doc_ident = #cargo_toml_contents_ident.parse::<toml_edit::Document>().unwrap();
147                #cargo_toml_doc_ident.fmt();
148                #cargo_toml_doc_ident.sort_values();
149                #cargo_toml_doc_ident.to_string()
150            };
151            std::io::Write::write_all(&mut #cargo_toml_file_ident, #cargo_toml_contents_ident.as_bytes()).unwrap();
152        }
153    )
154    .into()
155}