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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
#[macro_use] extern crate quote; extern crate itertools; extern crate proc_macro; extern crate syn; mod ast; mod generator; mod parser; mod scanner; mod token; use ast::Ast; use proc_macro::TokenStream; use std::fs::File; use std::io::prelude::*; use std::path::{Path, PathBuf}; fn user_crate_root() -> PathBuf { std::env::current_dir().expect("Unable to get current directory") } fn find_attr<'a>(attrs: &'a Vec<syn::Attribute>, name: &str) -> Option<&'a str> { attrs.iter() .find(|&x| x.name() == name) .and_then(|ref attr| match &attr.value { &syn::MetaItem::NameValue(_, syn::Lit::Str(ref template, _)) => Some(template), _ => None }) .map(|x| x.as_ref()) } fn buf_file<P: AsRef<Path>>(filename: P) -> String { let mut f = File::open(filename) .expect("Unable to open file for reading"); let mut buf = String::new(); f.read_to_string(&mut buf) .expect("Unable to read file"); buf } fn parse_str(input: &str) -> Result<Ast, parser::Error> { parser::parse(scanner::sequence(input).unwrap()) } struct InlinePartialsResolver; impl generator::PartialsResolver for InlinePartialsResolver { fn generate_partial(&mut self, _partial_name: &str) -> quote::Tokens { panic!("Partials are unavailable when using template_string"); } } struct FilesystemPartialsResolver<'a> { base_dir: PathBuf, dependencies: &'a mut Vec<String>, } impl<'a> FilesystemPartialsResolver<'a> { fn new<T: Into<PathBuf>>(base_dir: T, dependencies: &mut Vec<String>) -> FilesystemPartialsResolver { FilesystemPartialsResolver { base_dir: base_dir.into(), dependencies, } } } impl<'a> generator::PartialsResolver for FilesystemPartialsResolver<'a> { fn generate_partial(&mut self, partial_name: &str) -> quote::Tokens { let relative_path: PathBuf = partial_name.into(); let abs_path = match relative_path.has_root() { true => user_crate_root().join(relative_path.strip_prefix("/").unwrap()), false => self.base_dir.join(partial_name), }; self.dependencies.push(abs_path.to_str().unwrap().to_owned()); let template = buf_file(&abs_path); let parsed = parse_str(&template).unwrap(); let nested_resolver = &mut FilesystemPartialsResolver::new(abs_path.parent().unwrap(), self.dependencies); generator::generate(parsed, 1, nested_resolver) } } #[proc_macro_derive(BartDisplay, attributes(template, template_string, template_root))] pub fn bart_display(input: TokenStream) -> TokenStream { let s = input.to_string(); let ast = syn::parse_macro_input(&s).unwrap(); let mut dependencies = Vec::<String>::new(); let generated = { let (template, mut partials_resolver): (_, Box<generator::PartialsResolver>) = match find_attr(&ast.attrs, "template") { Some(filename) => { let abs_filename = user_crate_root().join(filename); dependencies.push(abs_filename.to_str().unwrap().to_owned()); let resolver = FilesystemPartialsResolver::new(abs_filename.parent().unwrap(), &mut dependencies); (buf_file(filename), Box::new(resolver)) }, None => { let template = find_attr(&ast.attrs, "template_string") .map(|x| x.to_owned()) .expect("#[derive(BartDisplay)] requires #[template = \"(filename)\"] \ or #[template_string = \"...\"]"); (template, Box::new(InlinePartialsResolver)) } }; let parsed = parse_str(&template).unwrap(); generator::generate(parsed, 1, &mut *partials_resolver) }; let template_root = syn::Ident::new(find_attr(&ast.attrs, "template_root") .map(|x| scanner::segmented_name(&x).expect("Syntax error in template_root")) .map(|x| format!("self.{}", x.join("."))) .unwrap_or("self".to_owned())); let name = &ast.ident; let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); let dummy_const = syn::Ident::new(format!("_IMPL_BART_DISPLAY_FOR_{}", &name)); let gen = quote! { #[allow(non_upper_case_globals, unused_attributes, unused_qualifications, unknown_lints, clippy)] const #dummy_const: () = { extern crate bart as _bart; #[automatically_derived] impl #impl_generics ::std::fmt::Display for #name #ty_generics #where_clause { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { #( let _ = include_bytes!(#dependencies); )* let _s0 = &#template_root; #generated Ok(()) } } }; }; gen.parse().unwrap() }