extern crate itertools;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate maplit;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;
#[macro_use] mod codegen;
mod files;
mod strings;
use std::collections::HashMap;
use std::env;
use std::error::Error;
use std::fs;
use std::io::{self, BufReader};
use std::path::Path;
use itertools::Itertools;
use self::files::create_out_file;
use self::strings::upper_camel_case;
fn main() {
generate_currency_code().unwrap();
if is_feature_enabled("mods_db") {
generate_item_mod_code().unwrap();
}
}
fn is_feature_enabled(name: &str) -> bool {
let var = format!("CARGO_FEATURE_{}", name.replace("-", "_").to_uppercase());
env::var(&var).is_ok()
}
const CURRENCY_JSON_FILES: &[&str] = &["data/currency.json", "data/extra/currency.json"];
const CURRENCY_ENUM_FILE: &str = "model/currency/enum.inc.rs";
const CURRENCY_DE_FILE: &str = "model/de/currency/visit_str.inc.rs";
lazy_static! {
static ref ADDITIONAL_CURRENCY_IDS: HashMap<&'static str, &'static [&'static str]> = hashmap!{
"exalted" => &["exa"] as &[&str],
"fusing" => &["fuse"],
"mir" => &["mirror"],
"splinter-xoph" => &["splinter-of-xoph"],
"splinter-tul" => &["splinter-of-tul"],
"splinter-esh" => &["splinter-of-esh"],
"splinter-uul-netol" => &["splinter-of-uul-netol"],
"splinter-chayula" => &["splinter-of-chayula"],
};
}
fn generate_currency_code() -> Result<(), Box<Error>> {
let mut all_currencies = Vec::new();
for &path in CURRENCY_JSON_FILES.iter() {
let data_file = Path::new(".").join(path);
let mut file = fs::OpenOptions::new().read(true).open(data_file)?;
let currencies: Vec<CurrencyData> = serde_json::from_reader(&mut file)?;
all_currencies.extend(currencies);
}
generate_currency_enum(&all_currencies)?;
generate_currency_de(&all_currencies)?;
Ok(())
}
#[derive(Debug, Deserialize)]
struct CurrencyData {
image: String,
#[serde(rename = "text")]
name: String,
id: String,
}
fn generate_currency_enum(currencies: &[CurrencyData]) -> io::Result<()> {
let out = create_out_file(CURRENCY_ENUM_FILE)?;
let mut ctx = codegen::Context::new(out);
ctx.emit("/// Currency item used for trading.")?;
ctx.emit("#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize)]")?;
ctx.begin("pub enum Currency {")?;
for currency in currencies {
emit!(ctx, "/// {}.", currency.name)?;
emit!(ctx, "#[serde(rename=\"{}\")]", currency.id)?;
emit!(ctx, "{},", upper_camel_case(¤cy.name))?;
}
ctx.end("}")?;
Ok(())
}
fn generate_currency_de(currencies: &[CurrencyData]) -> io::Result<()> {
let out = create_out_file(CURRENCY_DE_FILE)?;
let mut ctx = codegen::Context::new(out);
ctx.begin("match v {")?;
for currency in currencies {
let mut patterns: Vec<&str> = vec![¤cy.id];
if let Some(more) = ADDITIONAL_CURRENCY_IDS.get(¤cy.id.as_str()) {
patterns.extend(more.iter());
}
emit!(ctx, "{} => Ok(Currency::{}),",
patterns.into_iter().format_with(" | ", |x, f| f(&format_args!("\"{}\"", x))),
upper_camel_case(¤cy.name))?;
}
ctx.emit("v => Err(de::Error::invalid_value(Unexpected::Str(v), &EXPECTING_MSG)),")?;
ctx.end("}")?;
Ok(())
}
const ITEM_MODS_DATA_DIR: &str = "data/mods";
const ITEM_MODS_TYPES: &[&str] = &["crafted", "enchant", "explicit", "implicit"];
const ITEM_MODS_DATABASE_FILE: &str = "model/item/mods/database/by_type_and_id.inc.rs";
const READ_BUFFER_SIZE: usize = 64 * 1024;
fn generate_item_mod_code() -> Result<(), Box<Error>> {
generate_item_mods_mapping()?;
Ok(())
}
fn generate_item_mods_mapping() -> io::Result<()> {
let out = create_out_file(ITEM_MODS_DATABASE_FILE)?;
let mut ctx = codegen::Context::new(out);
ctx.begin("{")?;
ctx.emit("let mut hm = HashMap::new();")?;
for &mod_type in ITEM_MODS_TYPES.iter() {
ctx.begin("hm.insert(")?;
emit!(ctx, r#"ModType::from_str("{}").unwrap(),"#, mod_type)?;
generate_item_mods_mapping_for_type(&mut ctx, mod_type)?;
ctx.end(");")?;
}
ctx.emit("hm")?;
ctx.end("}")?;
Ok(())
}
fn generate_item_mods_mapping_for_type<W: io::Write>(
ctx: &mut codegen::Context<W>, mod_type: &str
) -> io::Result<()> {
let data_file = Path::new(".").join(ITEM_MODS_DATA_DIR).join(mod_type).with_extension("json");
let file = fs::OpenOptions::new().read(true).open(data_file)?;
let reader = BufReader::with_capacity(READ_BUFFER_SIZE, file);
let mods: Vec<ItemModData> = serde_json::from_reader(reader)?;
ctx.begin("{")?;
ctx.emit("let mut hm = HashMap::new();")?;
for imd in mods {
let text = imd.text.replace('\n', " ");
ctx.begin("hm.insert(")?;
emit!(ctx, r#"ModId::from_str("{}").unwrap(),"#, imd.id)?;
emit!(ctx, r#"Arc::new(ModInfo::from_raw("{}", "{}").unwrap()),"#,
imd.id, text)?;
ctx.end(");")?;
}
ctx.emit("hm")?;
ctx.end("}")?;
Ok(())
}
#[derive(Debug, Deserialize)]
struct ItemModData {
#[serde(rename = "type")]
type_: String,
text: String,
id: String,
}