#![recursion_limit = "1024"] // quote :D // https://imgur.com/zAQHVPh.png #[macro_use] extern crate procedural_masquerade; extern crate proc_macro; // FIXME: Switch to syn. This can wait for macros2.0. extern crate syntex; extern crate syntex_syntax; #[macro_use] extern crate quote; extern crate rustfmt; mod parse; mod table; mod output; define_proc_macros! { #[allow(non_snake_case)] pub fn __v11_internal_table(macro_args: &str) -> String { use syntex_syntax::parse::{ParseSess, new_parser_from_source_str}; let macro_args = macro_args.to_owned(); let sess = ParseSess::new(); let mut parser = new_parser_from_source_str(&sess, "".to_owned(), macro_args); let mut table = match ::parse::parse_table(&mut parser) { Err(msg) => { let mut diagnostic = msg.into_diagnostic(); diagnostic.cancel(); panic!("{}", diagnostic.message()); }, Ok(t) => t, }; if let Some(err) = table.validate() { error(err); return String::new(); } use std::env::var as env; let output_path = env("V11_MACRO_DUMP_DIR").ok().unwrap_or_else(|| { // FIXME: There doesn't seem to be a proper way of doing this. // We really want 'release' vs 'target', but that seems inaccessible // without egregious hacks. format!( "{}/target/v11_dump/{}_{}.rs", env("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"), table.domain, table.name, ) }); let output_path = ::std::path::Path::new(&output_path); let dump = match env("V11_MACRO_DUMP").as_ref().map(String::as_str) { Ok("*") => true, Ok("0") | Ok("") => false, Ok(n) => n == table.name, _ => false, }; let mut ret = Vec::new(); ::output::write_out(table, &mut ret).unwrap(); let ret = String::from_utf8(ret).unwrap(); if dump { // FIXME: Freak out if output file already exists & is newer than process. // formatting let formatted = { use rustfmt::*; let mut formatted = Vec::new(); let (_, mut filemap, _) = format_input( Input::Text(ret), &config::Config::default(), Some(&mut formatted), ).unwrap(); let out = filemap.pop().unwrap(); out.1.to_string() }; use std::fs; use std::io::Write; fs::create_dir_all(output_path.parent().expect("no parent")).ok(); let mut out = fs::File::create(output_path).expect("unable to create dump file"); out.write(formatted.as_bytes()).expect("dump failed"); format!("include!({:?});", output_path) } else { ret } } } use std::fmt::Display; #[allow(dead_code)] fn warn(msg: S) { println!("{}", msg); /* for line in msg.as_ref().split("\n") { println!("cargo:warning={}", line); }*/ } #[allow(dead_code)] fn error(msg: S) { warn(msg); }