1extern crate proc_macro;
2
3use std::fmt::Write;
4
5#[proc_macro]
6pub fn templing(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
7 let input: syn::LitStr = syn::parse(input).expect("Expected a string literal as input");
8 templing_impl(input.value().as_str(), Vec::new())
9 .parse()
10 .unwrap()
11}
12
13#[proc_macro]
14pub fn dbg_templing(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
15 let input: syn::LitStr = syn::parse(input).expect("Expected a string literal as input");
16 panic!("{}", templing_impl(input.value().as_str(), Vec::new()));
17}
18
19#[proc_macro]
20pub fn include_templing(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
21 let input: syn::LitStr = syn::parse(input).expect("Expected a string literal as input");
22 let cargo_manifest_dir =
23 std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir");
24 let path = std::path::Path::new(&cargo_manifest_dir).join(input.value());
25 let input = std::fs::read_to_string(&path).expect(&format!("Failed to read {:?}", path));
26 templing_impl(input.as_str(), vec![path]).parse().unwrap()
27}
28
29#[proc_macro]
30pub fn dbg_include_templing(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
31 let input: syn::LitStr = syn::parse(input).expect("Expected a string literal as input");
32 let cargo_manifest_dir =
33 std::env::var("CARGO_MANIFEST_DIR").expect("Failed to get cargo manifest dir");
34 let path = std::path::Path::new(&cargo_manifest_dir).join(input.value());
35 let input = std::fs::read_to_string(&path).expect(&format!("Failed to read {:?}", path));
36 panic!("{}", templing_impl(input.as_str(), vec![path]));
37}
38
39fn templing_impl(input: &str, file_dependencies: Vec<std::path::PathBuf>) -> String {
40 let mut result = String::new();
41 writeln!(&mut result, "{{").unwrap();
42 for file in file_dependencies {
43 writeln!(&mut result, "include_bytes!({:?});", file).unwrap();
44 }
45 writeln!(&mut result, "let mut templing_result = String::new();").unwrap();
46 let mut current_line = 0;
47 for line in input.lines() {
48 current_line += 1;
49 let non_ws = line.trim();
50 if let Some(code) = non_ws.strip_prefix("- ") {
51 writeln!(&mut result, "{}", code).unwrap();
52 } else {
53 let mut current_column = 1;
54 let mut line = line.to_owned();
55 if line.trim().starts_with('\\') {
56 line = line.replacen('\\', "", 1);
57 current_column += 1;
58 }
59 let mut line = line.as_str();
60 let mut write_eol = true;
61 if line.trim().starts_with('~') {
62 let index = line.find('~').unwrap();
63 current_column += line[index + 1..].chars().count();
64 line = &line[index + 1..];
65 }
66 if line.trim().ends_with('~') {
67 let index = line.rfind('~').unwrap();
68 line = &line[..index];
69 write_eol = false;
70 }
71 while !line.is_empty() {
72 let index = match line.find("{{") {
73 Some(index) => index,
74 None => line.len(),
75 };
76 writeln!(
77 &mut result,
78 "templing_result.push_str({:?});",
79 &line[..index],
80 )
81 .unwrap();
82 if index < line.len() {
83 current_column += line[..index + 2].chars().count();
84 writeln!(&mut result, "let templing_indentation = (templing_result.len() - templing_result.rfind('\\n').map(|pos| pos + 1).unwrap_or(0));").unwrap();
85 line = &line[index + 2..];
86 let index = line.find("}}").expect(&format!(
87 "Failed to find closing brackets for {}:{}",
88 current_line,
89 current_column - 2,
90 ));
91 let code = &line[..index];
92 if code.chars().next() == Some('#') {
93 writeln!(&mut result, "{}", &code[1..]).unwrap();
94 } else {
95 writeln!(
96 &mut result,
97 "let templing_value = {{ {} }}.to_string();",
98 code,
99 )
100 .unwrap();
101 writeln!(&mut result, "let templing_value = templing_value.trim();")
102 .unwrap();
103 writeln!(&mut result, "for (templing_part_index, templing_part) in templing_value.split('\\n').enumerate() {{").unwrap();
104 writeln!(&mut result, "if templing_part_index != 0 {{").unwrap();
105 writeln!(&mut result, "templing_result.push('\\n');").unwrap();
106 writeln!(&mut result, "for _ in 0..templing_indentation {{").unwrap();
107 writeln!(&mut result, "templing_result.push(' ');").unwrap();
108 writeln!(&mut result, "}}").unwrap();
109 writeln!(&mut result, "}}").unwrap();
110 writeln!(&mut result, "templing_result.push_str(templing_part);").unwrap();
111 writeln!(&mut result, "}}").unwrap();
112 }
113 current_column += line[..index + 2].chars().count();
114 line = &line[index + 2..];
115 } else {
116 line = "";
117 }
118 }
119 if write_eol {
120 writeln!(&mut result, "templing_result.push('\\n');").unwrap();
121 }
122 }
123 }
124 writeln!(&mut result, "templing_result.trim().to_owned()").unwrap();
125 writeln!(&mut result, "}}").unwrap();
126 result
127}