use core::fmt; use std::fs; use std::io::{BufWriter, Write}; use serde::Deserialize; fn main() { println!("cargo:rerun-if-changed=data/*"); if let Err(e) = gen_data_mod() { eprintln!("Error: {}", e); } } fn gen_data_mod() -> Result<(), std::io::Error> { let file = fs::File::create("src/data.rs")?; let mut buf = BufWriter::new(file); writeln!(buf, "use crate::types::*;\n")?; println!("cargo:rerun-if-changed=data/ingredients/*.toml"); write_ingredients(&mut buf)?; println!("cargo:rerun-if-changed=data/recipes/*.toml"); write_recipes(&mut buf)?; Ok(()) } #[derive(Deserialize, Debug)] pub struct Main { ingredients: Option>, recipes: Option>, } #[allow(dead_code)] #[derive(Deserialize, Debug)] pub struct Ingredient { key: String, translates: IngredientTranslate, } #[allow(dead_code)] #[derive(Deserialize, Debug)] pub struct IngredientTranslate { rus: String, eng: Option, } #[derive(Deserialize)] pub struct Recipe { key: String, ingredients: Vec, steps: u8, translates: RecipeTranslates, } impl fmt::Debug for Recipe { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Recipe") .field("key", &self.key) .field("steps", &self.steps) .field("ingredients", &format_args!("vec!{:#?}", self.ingredients)) .field("translates", &self.translates) .finish() } } #[derive(Deserialize)] pub struct RecipeIngredient { key: String, #[serde(flatten)] measure: RecipeIngredientMeasure, } impl fmt::Debug for RecipeIngredient { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RecipeIngredient") .field("key", &self.key) .field("measure", &format_args!("Measure::{:?}", self.measure)) .finish() } } #[derive(Deserialize, Debug)] pub enum RecipeIngredientMeasure { #[serde(rename = "g")] Gram(u32), #[serde(rename = "kg")] KiloGram(u32), #[serde(rename = "ml")] MilliLiter(u32), #[serde(rename = "l")] Liter(u32), } #[allow(dead_code)] #[derive(Deserialize, Debug)] pub struct RecipeTranslates { rus: RecipeTranslate, eng: Option, } #[derive(Deserialize)] pub struct RecipeTranslate { name: String, instructions: Vec, } impl fmt::Debug for RecipeTranslate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RecipeTranslate") .field("name", &self.name) .field( "instructions", &format_args!("vec!{:#?}", self.instructions), ) .finish() } } fn write_ingredients(file: &mut BufWriter) -> Result<(), std::io::Error> { let ingredients = get_ingredient_configs()?; writeln!( file, "pub const INGREDIENTS: [Ingredient; {}] = {:#?};\n", ingredients.len(), ingredients )?; Ok(()) } fn get_ingredient_configs() -> Result, std::io::Error> { Ok(fs::read_dir("data/ingredients")? .map(|res| res.and_then(|e| fs::read_to_string(e.path()))) .collect::, std::io::Error>>()? .into_iter() .map(|content| toml::from_str(&content).unwrap()) .filter_map(|cfg: Main| cfg.ingredients) .flatten() .collect::>()) } fn write_recipes(file: &mut BufWriter) -> Result<(), std::io::Error> { let recipes = get_recipe_configs()?; writeln!( file, r#"lazy_static::lazy_static! {{ pub static ref RECIPES: [Recipe; {}] = {:#?}; }}"#, recipes.len(), recipes )?; Ok(()) } fn get_recipe_configs() -> Result, std::io::Error> { Ok(fs::read_dir("data/recipes")? .map(|res| res.and_then(|e| fs::read_to_string(e.path()))) .collect::, std::io::Error>>()? .into_iter() .map(|content| toml::from_str(&content).unwrap()) .filter_map(|cfg: Main| cfg.recipes) .flatten() .collect::>()) }