#![allow(unused_imports)]
use std::path::PathBuf;
use std::fs;
use std::str::FromStr;
use std::collections::HashMap;
use std::convert::From;
use rustc_serialize::json;
use rustc_serialize::json::Json;
use rustc_serialize::Decodable;
use syntax::codemap::Span;
use syntax::ast::{Path, ExprLit, Lit_, TokenTree, TtToken};
use syntax::parse::token;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager};
use syntax::ext::source_util::expand_file;
#[derive(RustcDecodable)]
struct CharRef {
codepoints: Vec<u32>,
}
fn build_map(js: Json) -> Option<HashMap<String, [u32; 2]>> {
let mut map = HashMap::new();
let json_map = match js {
Json::Object(m) => m,
_ => return None,
};
for (k,v) in json_map.into_iter() {
let mut decoder = json::Decoder::new(v);
let CharRef { codepoints }: CharRef
= Decodable::decode(&mut decoder).ok().expect("bad CharRef");
assert!((codepoints.len() >= 1) && (codepoints.len() <= 2));
let mut codepoint_pair = [0, 0];
for (i,n) in codepoints.into_iter().enumerate() {
codepoint_pair[i] = n;
}
assert!(k.chars().next() == Some('&'));
map.insert(k[1..].to_string(), codepoint_pair);
}
map.insert("".to_string(), [0, 0]);
let keys: Vec<String> = map.keys().map(|k| k.to_string()).collect();
for k in keys.into_iter() {
for n in 1 .. k.len() {
let pfx = k[..n].to_string();
if !map.contains_key(&pfx) {
map.insert(pfx, [0, 0]);
}
}
}
Some(map)
}
pub fn expand(cx: &mut ExtCtxt, sp: Span, tt: &[TokenTree]) -> Box<MacResult+'static> {
let usage = "Usage: named_entities!(\"path/to/entities.json\")";
let json_filename = match tt {
[TtToken(_, token::Literal(token::Lit::Str_(s), _))] => s.as_str().to_string(),
_ => ext_bail!(cx, sp, usage),
};
let mod_filename = ext_expect!(cx, sp, match expand_file(cx, sp, &[]).make_expr() {
Some(e) => match e.node {
ExprLit(ref s) => match s.node {
Lit_::LitStr(ref s, _) => Some(s.to_string()),
_ => None,
},
_ => None,
},
_ => None,
}, "unexpected result from file!()");
let mut path: PathBuf = From::from(&mod_filename);
path.pop();
path.push(&json_filename);
let mut json_file = ext_expect!(cx, sp, fs::File::open(&path).ok(),
"can't open JSON file");
let js = ext_expect!(cx, sp, Json::from_reader(&mut json_file).ok(),
"can't parse JSON file");
let map = ext_expect!(cx, sp, build_map(js),
"JSON file does not match entities.json format");
let toks: Vec<_> = map.into_iter().flat_map(|(k, [c0, c1])| {
let k = &k[..];
(quote_tokens!(&mut *cx, $k => [$c0, $c1],)).into_iter()
}).collect();
MacEager::expr(quote_expr!(&mut *cx, phf_map!($toks)))
}