#![allow(dead_code)] use std::collections::HashMap; use askama::Template; use itertools::Itertools; use serde::Deserialize; use tracing_subscriber::EnvFilter; #[derive(Debug, Deserialize, PartialEq, Eq)] enum Character { #[serde(alias = "A")] Aqua, #[serde(alias = "V")] Ventus, #[serde(alias = "T")] Terra, } #[derive(Debug, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)] struct Ability { name: String, from: String, category: String, max: u8, types: Vec, } #[derive(Debug, Deserialize)] struct Command { name: String, category: String, char: Vec, #[serde(default)] starting: Option, #[serde(default)] info: Option, #[serde(default)] recipes: Vec, } impl Command {} #[derive(Debug, Deserialize)] struct CommandRecipe { char: Vec, r#type: char, ingredients: (String, String), chance: u8, #[serde(skip_deserializing)] abilities: Vec<(String, Ability)>, } impl CommandRecipe { pub fn get_ability(&self, crystal: &str) -> Option { if self.r#type == '-' { return None; } for ability in self.abilities.iter() { if ability.0 == crystal { return Some(ability.1.clone()); } } // This should never happen unless the json files are wrong panic!( "No ability found for {} + {} and {}", self.ingredients.0, self.ingredients.1, crystal ); } pub fn can_unlock(&self, char: Character) -> bool { self.char.contains(&char) } pub fn get_unlock_chars(&self) -> String { let mut id = String::new(); if self.can_unlock(Character::Terra) { id += "T" } if self.can_unlock(Character::Ventus) { if !id.is_empty() { id += " "; } id += "V"; } if self.can_unlock(Character::Aqua) { if !id.is_empty() { id += " "; } id += "A"; } id } pub fn set_abilities(&mut self, abilities: &[Ability]) { let mut vec: Vec<(String, Ability)> = vec![]; for ability in abilities.iter() { if ability.types.contains(&self.r#type) { vec.push((ability.from.clone(), ability.clone())); } } vec.sort(); self.abilities = vec; } } #[derive(Debug, Deserialize)] struct Finisher { name: String, char: Vec, level: u8, #[serde(default)] follows: Vec, goal: String, color: String, } #[derive(Template)] #[template(path = "pages/commands.html", whitespace = "suppress")] struct CommandsTemplate { pub commands: Vec, pub crystals: Vec, } const ABILITIES_PATH: &str = "./input/abilities.json"; const FINISHERS_PATH: &str = "./input/finish-commands.json"; const COMMANDS_PATH: &str = "./input/commands.json"; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); fn main() { // Initialize tracing tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .event_format(tracing_subscriber::fmt::format().with_source_location(true)) .init(); tracing::info!("Loading abilities json data from {}", ABILITIES_PATH); let abilities_str = std::fs::read_to_string(ABILITIES_PATH).unwrap(); let abilities = serde_json::from_str::>(&abilities_str).unwrap(); tracing::info!("Loading finishers json data from {}", ABILITIES_PATH); let finishers_str = std::fs::read_to_string(FINISHERS_PATH).unwrap(); let finishers = serde_json::from_str::>(&finishers_str).unwrap(); tracing::info!("Loading commands json data from {}", ABILITIES_PATH); let commands_str = std::fs::read_to_string(COMMANDS_PATH).unwrap(); let mut commands = serde_json::from_str::>(&commands_str).unwrap(); // Create a vec with all the crystal variants found in abilities let crystals = abilities .iter() .map(|x| x.from.clone()) .unique() .sorted() .collect(); // Create a vec of crystals and what ability they give for each recipe for cmd in commands.iter_mut() { for recipe in cmd.recipes.iter_mut() { recipe.set_abilities(&abilities); } } tracing::info!("Generating the commands table template"); let template = CommandsTemplate { commands, crystals }; std::fs::write("./index.html", template.render().unwrap()).unwrap(); }