use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; use crate::models::recipe::*; use crate::recipe_functions::common::{self, *}; /// Version 2 of `models::Recipe`, WIP /// #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct VersionRecipe { pub currentVersion: Value, pub knownVersions: Vec, pub recipesByCategory: HashMap>, } impl VersionRecipe { // import - read all file with given format from directory and mixing them together as 1 recipe // pub fn create_versioning_by_country(country: &str) -> Self { // let mut path = String::from(get_config().get("RECIPE_DIR").unwrap()); // path.push_str(country); // path.push('/'); // // read dir // let files = read_all_files_in_directory(&path.clone()); // let expected_file_format = "coffeethai02"; // let filtered = filter_files_by_pattern(files, expected_file_format); // // create recipes vector // let mut recipes = Vec::new(); // let mut known_versions = Vec::new(); // for file in filtered { // // try create model // let mut source_path = path.clone(); // source_path.push_str(&file); // let current_recipe_path = create_recipe_model_from_file(source_path); // known_versions.push(current_recipe_path.clone().MachineSetting.configNumber); // recipes.push(current_recipe_path.clone()); // } // // find current // // // let versions = grep_latest_versions(&path.clone()).unwrap(); // let expected_version = versions.get(country).unwrap_or(&0); // VersionRecipe { // currentVersion: serde_json::json!(format!("{}", expected_version)), // knownVersions: known_versions, // recipesByCategory: break_down_recipe_into_category(recipes), // } // } // export // export_full // export_full_with_history } pub fn break_down_recipe_into_category(recipes: Vec) -> HashMap> { let mut result = HashMap::new(); // Steps // 1. result } /// Based on `models::Recipe`, this excluded some fields /// for more efficient serialization and deserialization. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct RecipeV2 { pub Description: Option, pub LastChange: Option, pub id: Value, pub isUse: bool, pub name: Option, pub otherName: Option, pub productCode: String, pub recipes: Vec, pub ToppingSet: Option>, pub total_time: Value, pub changeLog: Vec>, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct RecipeListWithHistory { pub recipeList: RecipeList, pub history: Vec>, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] pub struct MenuToppingListWithHistory { pub menuToppingList: MenuToppingList, pub history: Vec>, } #[derive(Debug, Serialize, Deserialize, Clone)] pub struct RecipeListChange { pub new_value: Value, pub raw_old: Vec, pub raw_new: Vec, /// for sub menu pub sub_changes: HashMap, } impl RecipeListChange { pub fn get_changes(&mut self) { let old_length = self.raw_old.len(); let new_length = self.raw_new.len(); let is_match_length = old_length == new_length; let length_for_checking = if is_match_length { old_length } else { if old_length < new_length { new_length } else { old_length } }; let mut checked = Vec::with_capacity(length_for_checking); for ci in 0..length_for_checking { let current_repl = (self.raw_old.get(ci), self.raw_new.get(ci)); if current_repl.0.is_some() && current_repl.1.is_some() { let old_repl = current_repl.0.unwrap(); let new_repl = current_repl.1.unwrap(); let diff = old_repl.compare(new_repl); *checked.get_mut(ci).unwrap() = diff; } else if current_repl.0.is_none() && current_repl.1.is_some() { // new value *checked.get_mut(ci).unwrap() = current_repl .1 .unwrap() .to_map() .as_object() .unwrap() .keys() .map(|k| k.to_string()) .collect(); } else if current_repl.0.is_some() && current_repl.1.is_none() { // new value does not exist *checked.get_mut(ci).unwrap() = current_repl .0 .unwrap() .to_map() .as_object() .unwrap() .keys() .map(|k| format!("old.{}", k).to_string()) .collect(); } } self.new_value = serde_json::to_value(checked).unwrap(); } } /// break down recipe(s) in the folder into mapping, /// tracking diff between fields. (This does not compare other fields than Recipe01) pub fn build_recipe_shardings(source: PathBuf) -> HashMap { let files = common::get_all_files_in_directory(source.as_path().to_str().unwrap()); let mut recipes: Vec = files .par_iter() .filter(|file| file.ends_with("json") && file.contains("coffeethai02")) .map(|file| common::create_recipe_model_from_file(file.to_string())) .collect(); // ascending order a.compare(b) recipes.sort_by(|a, b| { let version_a = a.MachineSetting.get_config_number().as_i64().unwrap(); let version_b = b.MachineSetting.get_config_number().as_i64().unwrap(); version_a.cmp(&version_b) }); // Structure // { productCode: { version: [ Option ] } } let mut shards = HashMap::new(); if recipes.is_empty() { eprintln!("recipes get empty!!!"); } else { println!("recipes cnt = {}", recipes.len()); } println!( "first version from sorted = {:?}", recipes.first().unwrap().MachineSetting ); let base_version: i64 = recipes .first() .unwrap() .MachineSetting .get_config_number() .as_i64() .unwrap(); let base_recipe = recipes.get(0).unwrap(); shards = base_recipe .Recipe01 .par_iter() .map(|rp1| { let mut sub_changes = HashMap::new(); if rp1.clone().SubMenu.is_some() { let subs = rp1.clone().SubMenu.unwrap().clone(); sub_changes = subs .par_iter() .map(|sub1| { let change_sub = RecipeListChange { new_value: Value::Null, raw_new: sub1.recipes.clone(), raw_old: sub1.recipes.clone(), sub_changes: HashMap::new(), }; let mut change_sub_map = HashMap::new(); change_sub_map.insert(base_version, change_sub.clone()); recipes.iter().for_each(|rc| { // clone change let mut change_sub_clone = change_sub.clone(); match rc.search_pd_by_no_country_code(sub1.clone().productCode) { Some(spd) => { change_sub_clone.raw_new = spd.clone().recipes; } None => {} }; change_sub_clone.get_changes(); change_sub_map.insert( rc.MachineSetting .get_config_number() .as_i64() .unwrap() .clone(), change_sub_clone.clone(), ); }); ( sub1.clone().productCode, serde_json::to_value(change_sub_map).unwrap(), ) }) .collect(); } let change = RecipeListChange { new_value: Value::Null, raw_old: rp1.recipes.clone(), raw_new: rp1.recipes.clone(), sub_changes, }; let mut change_map = HashMap::new(); change_map.insert(base_version, change.clone()); recipes.iter().for_each(|rc| { // clone change let mut change_clone = change.clone(); match rc.search_pd_by_no_country_code(rp1.clone().productCode) { Some(pdr) => { change_clone.raw_new = pdr.clone().recipes; } None => {} } change_clone.get_changes(); change_map.insert( rc.MachineSetting .get_config_number() .as_i64() .unwrap() .clone(), change_clone, ); }); ( rp1.clone().productCode, serde_json::to_value(change_map).unwrap(), ) }) .collect(); shards }