add deep diff pd
This commit is contained in:
parent
64a7dae017
commit
6ccc6cb779
5 changed files with 67 additions and 53 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -143,7 +143,7 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libtbr"
|
name = "libtbr"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "libtbr"
|
name = "libtbr"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
@ -8,4 +8,4 @@ chrono = "0.4.41"
|
||||||
log = "0.4.27"
|
log = "0.4.27"
|
||||||
rayon = "1.10.0"
|
rayon = "1.10.0"
|
||||||
serde = { version = "1.0.219", features = ["derive", "serde_derive"] }
|
serde = { version = "1.0.219", features = ["derive", "serde_derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
49
README.md
49
README.md
|
|
@ -25,55 +25,6 @@ let recipe_dir = cfg.get("RECIPE_DIR").unwrap();
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
** Helper functions **
|
|
||||||
|
|
||||||
This will be included in the next version. So the following functions may just have to implement by yourself for now.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{self, Error, Read};
|
|
||||||
|
|
||||||
/// Get valid country names
|
|
||||||
fn valid_country_name() -> Vec<&'static str> {
|
|
||||||
vec!["mys", "sgp", "aus", "tha", "hkg", "dubai", "uae"]
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get latest versions of recipes
|
|
||||||
fn grep_latest_versions(dir_path: &str) -> Result<HashMap<String, usize>, io::Error> {
|
|
||||||
let mut vs = HashMap::new();
|
|
||||||
|
|
||||||
// open dir
|
|
||||||
let entries = std::fs::read_dir(dir_path)?
|
|
||||||
.map(|res| res.map(|e| e.path()))
|
|
||||||
.collect::<Result<Vec<_>, io::Error>>()?;
|
|
||||||
|
|
||||||
for e in entries {
|
|
||||||
let path = e.clone().to_str().unwrap().to_string();
|
|
||||||
let mut cl_path = path.clone();
|
|
||||||
let path_split = path.split("/").collect::<Vec<&str>>();
|
|
||||||
let dir_name = path_split[path_split.len() - 1];
|
|
||||||
|
|
||||||
if valid_country_name().contains(&dir_name) {
|
|
||||||
cl_path.push_str("/version");
|
|
||||||
|
|
||||||
// read filename
|
|
||||||
let mut file = File::open(cl_path)?;
|
|
||||||
let mut data = String::new();
|
|
||||||
file.read_to_string(&mut data).unwrap();
|
|
||||||
|
|
||||||
vs.insert(dir_name.to_string(), data.parse::<usize>().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
// expect dir with country
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(vs)
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
### Get recipe from specific country (latest)
|
### Get recipe from specific country (latest)
|
||||||
```rust
|
```rust
|
||||||
...
|
...
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ macro_rules! update_each_field {
|
||||||
/// about its structure.
|
/// about its structure.
|
||||||
/// * `MaterialCode`: MaterialCode is a vector (array) that contains instances of the MaterialCode
|
/// * `MaterialCode`: MaterialCode is a vector (array) that contains instances of the MaterialCode
|
||||||
/// struct.
|
/// struct.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct Recipe {
|
pub struct Recipe {
|
||||||
pub Timestamp: String,
|
pub Timestamp: String,
|
||||||
|
|
@ -91,6 +92,8 @@ impl Recipe {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get undefined fields from the recipe. This may included newer fields.
|
||||||
|
///
|
||||||
pub fn get_additional_fields(&self) -> Option<Value> {
|
pub fn get_additional_fields(&self) -> Option<Value> {
|
||||||
if self.extra.is_empty() {
|
if self.extra.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
|
|
@ -115,12 +118,14 @@ impl Recipe {
|
||||||
.find(|&r| r.productCode == product_code)
|
.find(|&r| r.productCode == product_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Similar to `search_pd` but ignoring exacted matching.
|
||||||
pub fn search_pd_by_no_country_code(&self, product_code: String) -> Option<&Recipe01> {
|
pub fn search_pd_by_no_country_code(&self, product_code: String) -> Option<&Recipe01> {
|
||||||
self.Recipe01
|
self.Recipe01
|
||||||
.iter()
|
.iter()
|
||||||
.find(|&r| r.productCode.contains(&product_code))
|
.find(|&r| r.productCode.contains(&product_code))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mulitple product code searching with parallel and no exact matching.
|
||||||
pub fn search_multi_in_parallel(&self, pds: Vec<String>) -> Vec<Option<Recipe01>> {
|
pub fn search_multi_in_parallel(&self, pds: Vec<String>) -> Vec<Option<Recipe01>> {
|
||||||
let mut found = Vec::new();
|
let mut found = Vec::new();
|
||||||
|
|
||||||
|
|
@ -136,6 +141,7 @@ impl Recipe {
|
||||||
found
|
found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get position of product code in the recipe's `Recipe01`
|
||||||
pub fn get_pd_index(&self, product_code: String) -> usize {
|
pub fn get_pd_index(&self, product_code: String) -> usize {
|
||||||
self.Recipe01
|
self.Recipe01
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -143,26 +149,32 @@ impl Recipe {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Search expected material setting if existed
|
||||||
pub fn search_material_settings(&self, material_code: String) -> Option<&MaterialSetting> {
|
pub fn search_material_settings(&self, material_code: String) -> Option<&MaterialSetting> {
|
||||||
self.MaterialSetting
|
self.MaterialSetting
|
||||||
.iter()
|
.iter()
|
||||||
.find(|r| r.id.to_string() == material_code)
|
.find(|r| r.id.to_string() == material_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Search expected topping list if existed
|
||||||
pub fn search_topping_list(&self, id: String) -> Option<&ToppingList> {
|
pub fn search_topping_list(&self, id: String) -> Option<&ToppingList> {
|
||||||
self.Topping.ToppingList.iter().find(|&r| r.id == id)
|
self.Topping.ToppingList.iter().find(|&r| r.id == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Search expected topping group if existed
|
||||||
pub fn search_topping_group(&self, id: String) -> Option<&ToppingGroup> {
|
pub fn search_topping_group(&self, id: String) -> Option<&ToppingGroup> {
|
||||||
self.Topping.ToppingGroup.iter().find(|&r| r.groupID == id)
|
self.Topping.ToppingGroup.iter().find(|&r| r.groupID == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Search expected material code if existed
|
||||||
pub fn search_material_code(&self, material_code: String) -> Option<&MaterialCode> {
|
pub fn search_material_code(&self, material_code: String) -> Option<&MaterialCode> {
|
||||||
self.MaterialCode
|
self.MaterialCode
|
||||||
.iter()
|
.iter()
|
||||||
.find(|&r| r.materialID == material_code)
|
.find(|&r| r.materialID == material_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if expected product code is different in both recipes.
|
||||||
|
/// This may return `false` if either one did not have this product code.
|
||||||
pub fn diff_pd_between_recipes(&self, another_recipe: &Recipe, product_code: String) -> bool {
|
pub fn diff_pd_between_recipes(&self, another_recipe: &Recipe, product_code: String) -> bool {
|
||||||
let menu1 = Self::search_pd(self, product_code.clone());
|
let menu1 = Self::search_pd(self, product_code.clone());
|
||||||
let menu2 = Self::search_pd(another_recipe, product_code.clone());
|
let menu2 = Self::search_pd(another_recipe, product_code.clone());
|
||||||
|
|
@ -177,6 +189,7 @@ impl Recipe {
|
||||||
menu1 == menu2
|
menu1 == menu2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Similar to `diff_pd_between_recipes` but will not check exacted matching.
|
||||||
pub fn diff_pd_between_recipes_ignore_country(
|
pub fn diff_pd_between_recipes_ignore_country(
|
||||||
&self,
|
&self,
|
||||||
another_recipe: &Recipe,
|
another_recipe: &Recipe,
|
||||||
|
|
@ -195,6 +208,35 @@ impl Recipe {
|
||||||
menu1 == menu2
|
menu1 == menu2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deep_diff_pd_between_recipes(
|
||||||
|
&self,
|
||||||
|
another_recipe: &Recipe,
|
||||||
|
product_code: String,
|
||||||
|
) -> Vec<(String, bool)> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
|
||||||
|
let self_recipe = Self::search_pd_by_no_country_code(self, product_code.clone());
|
||||||
|
let another_recipe =
|
||||||
|
Self::search_pd_by_no_country_code(another_recipe, product_code.clone());
|
||||||
|
|
||||||
|
if self_recipe.is_some() && another_recipe.is_some() {
|
||||||
|
let sr = self_recipe.unwrap();
|
||||||
|
let ar = another_recipe.unwrap();
|
||||||
|
|
||||||
|
let diff_field_list = sr.compare(ar);
|
||||||
|
|
||||||
|
for key in sr.to_map().keys() {
|
||||||
|
if diff_field_list.contains(key) {
|
||||||
|
result.push((key.to_string(), true));
|
||||||
|
} else {
|
||||||
|
result.push((key.to_string(), false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
pub fn diff_material_setting_between_recipes(
|
pub fn diff_material_setting_between_recipes(
|
||||||
&self,
|
&self,
|
||||||
another_recipe: &Recipe,
|
another_recipe: &Recipe,
|
||||||
|
|
@ -522,6 +564,7 @@ impl Recipe {
|
||||||
pub fn add_prefix_country_code(&mut self) {}
|
pub fn add_prefix_country_code(&mut self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn StrShowTextErrorDefault() -> Option<Vec<Value>> {
|
fn StrShowTextErrorDefault() -> Option<Vec<Value>> {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
Value::String("เต่าบินเกิดเหตุขัดข้อง".into()),
|
Value::String("เต่าบินเกิดเหตุขัดข้อง".into()),
|
||||||
|
|
@ -569,6 +612,7 @@ impl PartialRecipe {
|
||||||
/// temperature setting for a machine.
|
/// temperature setting for a machine.
|
||||||
/// * `temperatureMin`: The `temperatureMin` property is of type `Value`. It represents the minimum
|
/// * `temperatureMin`: The `temperatureMin` property is of type `Value`. It represents the minimum
|
||||||
/// temperature setting for the machine.
|
/// temperature setting for the machine.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct MachineSetting {
|
pub struct MachineSetting {
|
||||||
pub Comment: Option<Vec<String>>,
|
pub Comment: Option<Vec<String>>,
|
||||||
|
|
@ -659,26 +703,32 @@ impl CommonRecipeTrait for MachineSetting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn BlankString() -> Option<String> {
|
fn BlankString() -> Option<String> {
|
||||||
Some("".to_string())
|
Some("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn BlankBool() -> Option<bool> {
|
fn BlankBool() -> Option<bool> {
|
||||||
Some(false)
|
Some(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn BlankVecString() -> Option<Vec<String>> {
|
fn BlankVecString() -> Option<Vec<String>> {
|
||||||
Some(vec![].into())
|
Some(vec![].into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn BlankVecRecipe() -> Option<Vec<Recipe01>> {
|
fn BlankVecRecipe() -> Option<Vec<Recipe01>> {
|
||||||
Some(vec![].into())
|
Some(vec![].into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn BlankVecMenuTopping() -> Option<Vec<MenuToppingList>> {
|
fn BlankVecMenuTopping() -> Option<Vec<MenuToppingList>> {
|
||||||
Some(vec![].into())
|
Some(vec![].into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn BlankOtherStrShowTextError() -> Option<Vec<String>> {
|
fn BlankOtherStrShowTextError() -> Option<Vec<String>> {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
|
|
@ -692,14 +742,17 @@ fn BlankOtherStrShowTextError() -> Option<Vec<String>> {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn BlankValueString() -> Option<Value> {
|
fn BlankValueString() -> Option<Value> {
|
||||||
Some(Value::String("".into()))
|
Some(Value::String("".into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case, dead_code)]
|
||||||
fn BlankValueArray() -> Option<Value> {
|
fn BlankValueArray() -> Option<Value> {
|
||||||
Some(Value::Array(vec![].into()))
|
Some(Value::Array(vec![].into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case, dead_code)]
|
||||||
fn BlankValueBool() -> Option<Value> {
|
fn BlankValueBool() -> Option<Value> {
|
||||||
Some(Value::Bool(false.into()))
|
Some(Value::Bool(false.into()))
|
||||||
}
|
}
|
||||||
|
|
@ -754,6 +807,7 @@ fn BlankValueBool() -> Option<Value> {
|
||||||
/// * `useGram`: A boolean value indicating whether the recipe uses grams for measurement or not.
|
/// * `useGram`: A boolean value indicating whether the recipe uses grams for measurement or not.
|
||||||
/// * `weight_float`: The `weight_float` property is of type `Value` and represents the weight of the
|
/// * `weight_float`: The `weight_float` property is of type `Value` and represents the weight of the
|
||||||
/// recipe as a floating-point number.
|
/// recipe as a floating-point number.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct Recipe01 {
|
pub struct Recipe01 {
|
||||||
#[serde(default = "BlankString")]
|
#[serde(default = "BlankString")]
|
||||||
|
|
@ -1371,6 +1425,7 @@ impl Recipe01 {
|
||||||
/// water in the recipe.
|
/// water in the recipe.
|
||||||
/// * `waterYield`: The `waterYield` property represents the amount of water produced or obtained from
|
/// * `waterYield`: The `waterYield` property represents the amount of water produced or obtained from
|
||||||
/// the recipe.
|
/// the recipe.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct RecipeList {
|
pub struct RecipeList {
|
||||||
pub MixOrder: Value,
|
pub MixOrder: Value,
|
||||||
|
|
@ -1465,6 +1520,7 @@ impl RecipeList {
|
||||||
/// maximum number of retries for a payment.
|
/// maximum number of retries for a payment.
|
||||||
/// * `RawMaterialUnit`: The `RawMaterialUnit` property is an optional string that represents the unit
|
/// * `RawMaterialUnit`: The `RawMaterialUnit` property is an optional string that represents the unit
|
||||||
/// of measurement for the raw material.
|
/// of measurement for the raw material.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct MaterialSetting {
|
pub struct MaterialSetting {
|
||||||
pub AlarmIDWhenOffline: Value,
|
pub AlarmIDWhenOffline: Value,
|
||||||
|
|
@ -1576,6 +1632,7 @@ impl MachineSetting {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn BlankListGroupID() -> Option<Vec<Value>> {
|
fn BlankListGroupID() -> Option<Vec<Value>> {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
Value::String("0".into()),
|
Value::String("0".into()),
|
||||||
|
|
@ -1585,6 +1642,7 @@ fn BlankListGroupID() -> Option<Vec<Value>> {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
fn BlankGroupID() -> Option<Value> {
|
fn BlankGroupID() -> Option<Value> {
|
||||||
Some(Value::String("0".into()))
|
Some(Value::String("0".into()))
|
||||||
}
|
}
|
||||||
|
|
@ -1600,6 +1658,7 @@ fn BlankGroupID() -> Option<Value> {
|
||||||
/// which the menu topping list belongs.
|
/// which the menu topping list belongs.
|
||||||
/// * `isUse`: The `isUse` property is a boolean value that indicates whether the menu topping list is
|
/// * `isUse`: The `isUse` property is a boolean value that indicates whether the menu topping list is
|
||||||
/// being used or not.
|
/// being used or not.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct MenuToppingList {
|
pub struct MenuToppingList {
|
||||||
#[serde(default = "BlankListGroupID")]
|
#[serde(default = "BlankListGroupID")]
|
||||||
|
|
@ -1620,6 +1679,7 @@ pub struct MenuToppingList {
|
||||||
/// struct. Each ToppingGroup struct represents a group of toppings.
|
/// struct. Each ToppingGroup struct represents a group of toppings.
|
||||||
/// * `ToppingList`: The `ToppingList` property is a vector (dynamic array) that contains elements of
|
/// * `ToppingList`: The `ToppingList` property is a vector (dynamic array) that contains elements of
|
||||||
/// type `ToppingList`.
|
/// type `ToppingList`.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct Topping {
|
pub struct Topping {
|
||||||
pub ToppingGroup: Vec<ToppingGroup>,
|
pub ToppingGroup: Vec<ToppingGroup>,
|
||||||
|
|
@ -1643,6 +1703,7 @@ pub struct Topping {
|
||||||
/// * `name`: The `name` property is a string that represents the name of the topping group.
|
/// * `name`: The `name` property is a string that represents the name of the topping group.
|
||||||
/// * `otherName`: The `otherName` property is a string that represents an alternative name for the
|
/// * `otherName`: The `otherName` property is a string that represents an alternative name for the
|
||||||
/// `ToppingGroup`. It can be used to provide additional information or a different name for the group.
|
/// `ToppingGroup`. It can be used to provide additional information or a different name for the group.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct ToppingGroup {
|
pub struct ToppingGroup {
|
||||||
pub groupID: Value,
|
pub groupID: Value,
|
||||||
|
|
@ -1703,6 +1764,7 @@ impl ToppingGroup {
|
||||||
/// * `useGram`: A boolean value indicating whether the topping uses grams for measurement or not.
|
/// * `useGram`: A boolean value indicating whether the topping uses grams for measurement or not.
|
||||||
/// * `weight_float`: The `weight_float` property is of type `Value` and represents the weight of the
|
/// * `weight_float`: The `weight_float` property is of type `Value` and represents the weight of the
|
||||||
/// topping in float format.
|
/// topping in float format.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct ToppingList {
|
pub struct ToppingList {
|
||||||
pub ExtendID: Value,
|
pub ExtendID: Value,
|
||||||
|
|
@ -1754,6 +1816,7 @@ impl ToppingList {
|
||||||
/// * `materialCode`: The `materialCode` property is an optional field that represents the code
|
/// * `materialCode`: The `materialCode` property is an optional field that represents the code
|
||||||
/// associated with a material. It is of type `Option<String>`, which means it can either be
|
/// associated with a material. It is of type `Option<String>`, which means it can either be
|
||||||
/// `Some(String)` if a value is present, or `None` if no value is provided.
|
/// `Some(String)` if a value is present, or `None` if no value is provided.
|
||||||
|
#[allow(non_snake_case)]
|
||||||
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
|
||||||
pub struct MaterialCode {
|
pub struct MaterialCode {
|
||||||
#[serde(default = "BlankString")]
|
#[serde(default = "BlankString")]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use std::collections::HashMap;
|
||||||
use crate::models::recipe::*;
|
use crate::models::recipe::*;
|
||||||
use crate::recipe_functions::common::*;
|
use crate::recipe_functions::common::*;
|
||||||
|
|
||||||
/// Version 2 of `models::Recipe`
|
/// Version 2 of `models::Recipe`, WIP
|
||||||
///
|
///
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct VersionRecipe {
|
pub struct VersionRecipe {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue