tera_macro/
lib.rs

1extern crate proc_macro;
2
3use proc_macro2::TokenStream;
4use serde_json::Value;
5use syn::LitStr;
6use syn::parse::{Parse, ParseStream, Result};
7use tera::{Context, Tera};
8use unicode_segmentation::UnicodeSegmentation;
9
10struct TeraMacroInput {
11    context: LitStr,
12    rust_code: TokenStream,
13}
14
15impl Parse for TeraMacroInput {
16    fn parse(input: ParseStream) -> Result<Self> {
17        let json: LitStr = input.parse()?;
18        input.parse::<syn::token::Comma>()?;
19        let rust_code = input.parse()?;
20
21        Ok(TeraMacroInput { context: json, rust_code })
22    }
23}
24
25#[proc_macro]
26pub fn tera(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
27    let input = syn::parse_macro_input!(tokens as TeraMacroInput);
28    let template = remove_space_added_by_parsing(&input.rust_code.to_string());
29    println!("template: {:?}", template);
30    let mut tera = Tera::default();
31    tera.add_raw_template("tera", &template).expect("The template was not valid.");
32    let json_string: String = input.context.value();
33    let value: Value = serde_json::from_str(&json_string).expect("The Context was not valid json.");
34    println!("value: {:?}", value);
35    let context = Context::from_value(value).expect("Tera failed to create Context from json value.");
36    let output: String = tera.render("tera", &context).expect("Could not render the template");
37    println!("output: {:?}", output);
38    let token_stream: TokenStream = syn::parse_str(&output).expect("Could not converted the rendered output into a \
39    valid token stream");
40    proc_macro::TokenStream::from(token_stream)
41}
42
43
44fn remove_space_added_by_parsing(input: &str) -> String {
45    let graphemes = UnicodeSegmentation::graphemes(input, true);
46    let mut graphemes_it = graphemes.into_iter();
47    let Some(mut prev) = graphemes_it.next() else {
48        return String::new();
49    };
50    let Some(mut this) = graphemes_it.next() else {
51        return prev.into();
52    };
53    let mut result: Vec<&str> = Vec::with_capacity(input.len());
54    result.push(prev);
55
56    while let Some(next) = graphemes_it.next() {
57        match (prev, this, next) {
58            ("{", " ", "{") => (),
59            ("{", " ", "%") => (),
60            ("{", " ", "#") => (),
61            ("}", " ", "}") => (),
62            ("%", " ", "}") => (),
63            ("#", " ", "}") => (),
64            _ => result.push(this)
65        }
66        prev = this;
67        this = next;
68    }
69    result.push(this);
70    result.join("")
71}