1extern crate proc_macro;
2extern crate quote;
3extern crate syn;
4
5use proc_macro::TokenStream;
6use quote::quote;
7use std::path::Path;
8use syn::{parse_macro_input, ItemFn};
9
10struct Answer {
11 lesson_number: u32,
12 queries: Option<String>,
13 hql_src: Option<String>,
14}
15
16#[proc_macro_attribute]
17pub fn parse_answers(_attr: TokenStream, item: TokenStream) -> TokenStream {
18 let main_fn = parse_macro_input!(item as ItemFn);
19 let visibility = main_fn.vis;
20 let signature = main_fn.sig;
21 let body = main_fn.block;
22 let mut answers = Vec::new();
23
24 let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
25 let project_root = Path::new(&manifest_dir);
26
27 for lesson_number in 1..=24 {
29 let query_answer_path =
30 project_root.join(format!("query_answers/lesson{}.json", lesson_number));
31 let hql_answer_path =
32 project_root.join(format!("lesson_answers/lesson{}_queries.hx", lesson_number));
33
34 let lesson_answers = std::fs::read_to_string(&query_answer_path).ok();
35 let lesson_queries = std::fs::read_to_string(&hql_answer_path).ok();
36
37 answers.push(Answer {
38 lesson_number,
39 queries: lesson_answers,
40 hql_src: lesson_queries,
41 });
42 }
43
44 let lessons = answers.iter().map(
45 |Answer {
46 lesson_number,
47 queries,
48 hql_src,
49 }| {
50 let queries_value = queries.clone().unwrap_or(String::new());
51 let hql_src_value = hql_src.clone().unwrap_or(String::new());
52 quote! {
53 (#lesson_number, Lesson {
54 query_answer: #queries_value.to_string(),
55 hql_answer: #hql_src_value.to_string(),
56 })
57 }
58 },
59 );
60
61 let expanded = quote! {
62 use std::collections::HashMap;
63 pub struct Lesson {
64 pub query_answer: String,
65 pub hql_answer: String,
66 }
67 #visibility #signature {
68 let lessons = HashMap::from([
69 #(#lessons),*
70 ]);
71 #body
72 }
73 };
74
75 TokenStream::from(expanded)
76}