1use serde::{Serialize, Deserialize};
2use pathbufd::PathBufD;
3
4use std::sync::LazyLock;
5use std::fs::{read_to_string, File};
6
7mod generator;
8
9#[derive(Serialize, Deserialize, Clone)]
11struct Config {
12 pub root_dir: PathBufD,
21}
22
23static CONFIG: LazyLock<Config> = LazyLock::new(|| {
25 serde_json::from_str::<Config>(
26 &read_to_string(PathBufD::current().join("crml.json"))
27 .expect("failed to read configuration"),
28 )
29 .expect("failed to deserialize configuration")
30});
31
32use syn::{parse_macro_input, LitStr, ItemStruct};
34use quote::{quote, ToTokens};
35use proc_macro::TokenStream;
36use proc_macro2::TokenStream as TokenStream2;
37
38pub(crate) fn get_file(file: &str) -> File {
39 File::open(PathBufD::current().extend(&[CONFIG.root_dir.to_string(), format!("{}.crml", file)]))
40 .expect("failed to read included file")
41}
42
43#[proc_macro_attribute]
61pub fn template(args: TokenStream, input: TokenStream) -> TokenStream {
62 let args = parse_macro_input!(args as LitStr);
64 let file_name = args.value();
65
66 let input = parse_macro_input!(input as ItemStruct);
68
69 let struct_ident = input.ident.clone();
70 let mut struct_tokens = TokenStream2::new();
71 input.to_tokens(&mut struct_tokens);
72
73 let generated = generator::Generator::from_file(get_file(&file_name)).consume();
75
76 let generated_tokens: TokenStream2 = match generated.parse() {
77 Ok(t) => t,
78 Err(e) => {
79 if std::fs::exists("crml_dbg").expect("failed to check for debug dir") == true {
81 println!(
82 "Debug directory found. Check \"crml_dbg/{}.rs\" to debug template.",
83 file_name
84 );
85
86 std::fs::write(
87 format!("crml_dbg/{file_name}.rs"),
88 format!("fn debug() {{\n{generated}\n}}"),
89 )
90 .expect("failed to write debug file")
91 }
92
93 panic!("{}", e.to_string())
95 }
96 };
97
98 let expanded = quote! {
100 #struct_tokens
101
102 impl crml::Template for #struct_ident {
103 fn render(self) -> String {
104 #generated_tokens
105 }
106 }
107 };
108
109 if std::fs::exists("crml_dbg").expect("failed to check for debug dir") == true {
111 let file_name = file_name.replace("/", "_");
112 std::fs::write(format!("crml_dbg/{file_name}.rs"), expanded.to_string())
113 .expect("failed to write debug file")
114 }
115
116 expanded.into()
118}