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_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}