# libtbr `libtbr` is swiss-knife toolbox for Taobin project. ``` cargo add --git https://gitlab.forthrd.io/Pakin/libtbr.git ``` ``` cargo update ``` ## Examples ### Initialize Config ```rust use libtbr::recipe_functions::common; // this read file `.tbcfg` in the current directory let cfg = common::get_config(); let recipe_dir = cfg.get("RECIPE_DIR").unwrap(); ``` --- ### Get recipe from specific country (latest) ```rust ... let latest_versions = grep_latest_versions(recipe_dir).unwrap(); // try get malaysia recipe, may fail if country does not exist let mys_version = latest_versions.get("mys"); // try to create malaysia recipe model let mys_recipe_model = common::create_recipe_model_from_file(common::create_recipe_path( "mys", *mys_version.unwrap(), )); ... ``` ### Get list of material settings ```rust // get current material settings including empty (0) let mut used_in_mys = mys_recipe_model.list_material_settings(); used_in_mys.push("0".to_string()); ``` ### Get all recipe of expected material id ```rust let aus_latest = common::create_recipe_model_from_file(common::create_recipe_path( "aus", *aus_version.unwrap(), )); let prod_list = aus_latest.find_recipe_by_material_path_id("511214"); ``` ### Generate google sheet style table of recipe from file ```rust // Experimental: expect current execution path to have `./test_result` folder first, and `config.RECIPE_DIR/{country}/{version_file_format}` must exist. import::generate_recipe_sheet_table("mys", 626); ``` ### Notes Simple Snippet Patterns ```rust // ============================================= // Get current material and convert to thai id // ============================================= let curr_rpl_mat_id = rpl.materialPathId.as_i64().unwrap_or(0); // strip off first 2 let pure_mat_id = if curr_rpl_mat_id > 300000 { // curr_rpl_mat_id.to_string()[2..] .parse::() .expect("not a number") } else { curr_rpl_mat_id }; ``` --- ### XML Parser (Experimental) This will parse xml file and create node structure type `Vec` where `Node` is from `crate::xml::node` Xml-related functions - `parse_xml_to_tree`: this will parse raw xml string into node structure. - `print_tree`: printing node structure to xml - `generate_nodes_from_xml`: generate node index map from list of `(catalog_name, catalog_path)` Node functions - `find_by_child_value`: search expected value from node (`parent_node`) which this value should be in node named `child_name`. - `get_child`: get child node from current node's children Shortcut Macro - `get_path`: macro for accessing the node inner child by provided key `key1.key2.key3...` Example of parsing xml layout v3 and generate into `new-layout-v2` format ```rust use libtbr::xml::*; // ... let taobin_dir = cfg .get("TAOBIN_REPO") .expect("Taobin directory path not provided"); let v3_dir = format!("{taobin_dir}/inter/ltu/xml/multi/v3"); // pre-defined paths configuration in format (catalog_name, catalog_path) let v3_catalogs = vec![ ( "recommend", format!("{ltu_v3_dir}/event/event_v3/active_promotions.lxml"), ), ( "coffee", format!("{ltu_v3_dir}/page_catalog_group_coffee.lxml"), ), ("milk", format!("{ltu_v3_dir}/page_catalog_group_milk.lxml")), ("tea", format!("{ltu_v3_dir}/page_catalog_group_tea.lxml")), ( "health", format!("{ltu_v3_dir}/page_catalog_group_health.lxml"), ), ( "other", format!("{ltu_v3_dir}/page_catalog_group_other.lxml"), ), ]; // input must be `Vec<(&str, String)>` let mut v3_catalog_nodes: IndexMap> = generate_nodes_from_xml(v3_catalogs)?; for (_, (catalog_name, catalog_nodes)) in v3_catalog_nodes.iter().enumerate() { if catalog_nodes.len() == 1 && let Some(root_node) = catalog_nodes.first() { // get_path is a macro for accessing the node inner child // // usage: get_path!(root_node, key1.key2.key3...); // let current_menus_result: Option<&Node> = get_path!(root_node, ScrollableCatalog.Menus); if let Some(current_menus) = current_menus_result { println!( "Name={},file=page_catalog_group_{}.skt", catalog_name, catalog_name ); let ccm = current_menus.clone(); for menu_block in ccm.children.clone() { let mut name_row = String::from("\tname\t"); let mut desc_row = String::from("\tdesc\t"); let mut img_row = String::from("\timg\t"); let tag_filter_option = get_path!(menu_block, TagFilter); let idle_image_tag = match get_path!(menu_block, IdleImage) { Some(img_path) => { let img_path = img_path.clone().value.unwrap_or("".to_string()); let img_path_spl: Vec = img_path .trim() .replace("\"", "") .split("/") .map(|x| x.to_string()) .collect(); img_path_spl .last() .unwrap() .replace("[amp]", "&") .to_string() } None => "".to_string(), }; img_row.push_str(format!("{idle_image_tag}\t-\t-\t-\t\t\t||||||||||||||||||||||||||\t||||||||||||||||||||||||||\t||||||||||||||||||||||||||\t\t\t\t\t\t\t\t-\t-\t-\t-\t-\t").as_str()); let hot_state_val = match get_path!(menu_block, HotState) { Some(state) => state .clone() .value .and_then(|x| { if x.to_string().contains("Disable2") { return Some("-".to_string()); } else { return Some(x.replace("$", "").replace(".Button", "")); } }) .unwrap() .trim() .to_string(), None => "-".to_string(), }; let ice_state_val = match get_path!(menu_block, IceState) { Some(state) => state .clone() .value .and_then(|x| { if x.to_string().contains("Disable2") { return Some("-".to_string()); } else { return Some(x.replace("$", "").replace(".Button", "")); } }) .unwrap() .trim() .to_string(), None => "-".to_string(), }; let blend_state_val = match get_path!(menu_block, BlendState) { Some(state) => state .clone() .value .and_then(|x| { if x.to_string().contains("Disable2") { return Some("-".to_string()); } else { return Some(x.replace("$", "").replace(".Button", "")); } }) .unwrap() .trim() .to_string(), None => "-".to_string(), }; let names = match get_path!(menu_block, Name.LanguageGroup) { Some(names) => names.clone(), None => Node::default(), }; // Description let descs = match get_path!(menu_block, Description.LanguageGroup) { Some(descs) => descs.clone(), None => Node::default(), }; for name in names.children.clone() { if let Some(value) = name.value { name_row.push_str(format!("{value}\t").replace("[amp]", "&").as_str()); } else { name_row.push_str(format!("\t").as_str()); } } name_row.push_str( format!( "{},-\t{},-\t{},-\t\t\t\t\t\t\t\t-\t-\t-\t-\t{}", hot_state_val, ice_state_val, blend_state_val, tag_filter_option .clone() .unwrap_or(&Node::default()) .value .clone() .unwrap_or("-".to_string()) .trim() .replace("\"", "") ) .as_str(), ); for desc in descs.children.clone() { if let Some(value) = desc.value { desc_row.push_str(format!("{value}\t").replace("[amp]", "&").as_str()); } else { desc_row.push_str(format!("\t").as_str()); } } desc_row.push_str( format!( "||||||||||||||||||||||||||\t||||||||||||||||||||||||||\t||||||||||||||||||||||||||\t\t\t\t\t\t\t\t-\t-\t-\t-\t-\t" ) .as_str(), ); //|||||||||||||||||||||||||| println!("{name_row}"); println!("{desc_row}"); println!("{img_row}"); println!(""); } // name Americano อเมริกาโน Amerikano Americano 59-01-01-0003,59-21-01-0003 59-01-02-0001,59-21-02-0001 -,- - - Signature - CoffeeNoMilk,Recommend // desc Espresso, Water กาแฟ และน้ำ Espresas, vanduo Espresso, Apă |||||||||||||||||||||||||| |||||||||||||||||||||||||| |||||||||||||||||||||||||| - - - - - // img bn_hot_americano.png - bn_hot_america_no.png bn_hot_america_no.png posi1 |||||||||||||||||||||||||| |||||||||||||||||||||||||| - - - - - } } } ```