khguide/src/main.rs

174 lines
4.0 KiB
Rust
Raw Normal View History

#![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<char>,
}
#[derive(Debug, Deserialize)]
struct Command {
name: String,
category: String,
char: Vec<char>,
#[serde(default)]
starting: Option<bool>,
#[serde(default)]
info: Option<String>,
#[serde(default)]
recipes: Vec<CommandRecipe>,
}
impl Command {}
#[derive(Debug, Deserialize)]
struct CommandRecipe {
char: Vec<Character>,
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<Ability> {
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<Character>,
level: u8,
#[serde(default)]
follows: Vec<String>,
goal: String,
color: String,
}
#[derive(Template)]
#[template(path = "pages/commands.html", whitespace = "suppress")]
struct CommandsTemplate {
pub commands: Vec<Command>,
pub crystals: Vec<String>,
}
const ABILITIES_PATH: &str = "./input/abilities.json";
const FINISHERS_PATH: &str = "./input/finish-commands.json";
const COMMANDS_PATH: &str = "./input/commands.json";
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::<Vec<Ability>>(&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::<HashMap<String, Finisher>>(&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::<Vec<Command>>(&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();
}