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 123 124 125 126 127
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 current_column = 1; let mut line = line.to_owned(); if line.trim().starts_with('\\') { line = line.replacen('\\', "", 1); current_column += 1; } let mut line = line.as_str(); 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.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(); writeln!(&mut result, "let templing_indentation = (templing_result.len() - templing_result.rfind('\\n').map(|pos| pos + 1).unwrap_or(0));").unwrap(); 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, ) .unwrap(); writeln!(&mut result, "let templing_value = templing_value.trim();") .unwrap(); 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('\\n');").unwrap(); writeln!(&mut result, "for _ in 0..templing_indentation {{").unwrap(); writeln!(&mut result, "templing_result.push(' ');").unwrap(); writeln!(&mut result, "}}").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.trim().to_owned()").unwrap(); writeln!(&mut result, "}}").unwrap(); result }