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