///|
pub(all) enum MeasureKind {
Mass
Volume
Count
} derive(Eq, Debug)
///|
pub(all) enum MeasureUnit {
Gram
Kilogram
Milliliter
Liter
Teaspoon
Tablespoon
Cup
Piece
Slice
Pinch
} derive(Eq, Debug)
///|
pub fn unit_kind(unit : MeasureUnit) -> MeasureKind {
match unit {
Gram | Kilogram => Mass
Milliliter | Liter | Teaspoon | Tablespoon | Cup => Volume
Piece | Slice | Pinch => Count
}
}
///|
pub fn unit_text(unit : MeasureUnit) -> String {
match unit {
Gram => "g"
Kilogram => "kg"
Milliliter => "ml"
Liter => "l"
Teaspoon => "tsp"
Tablespoon => "tbsp"
Cup => "cup"
Piece => "piece"
Slice => "slice"
Pinch => "pinch"
}
}
///|
fn canonical_factor(unit : MeasureUnit) -> Int {
match unit {
Gram => 1
Kilogram => 1000
Milliliter => 1
Liter => 1000
Teaspoon => 5
Tablespoon => 15
Cup => 240
Piece => 1
Slice => 1
Pinch => 1
}
}
///|
pub(all) struct Quantity {
amount : Int
unit : MeasureUnit
} derive(Eq, Debug)
///|
pub fn Quantity::Quantity(
amount? : Int = 0,
unit? : MeasureUnit = Gram,
) -> Quantity {
{ amount, unit }
}
///|
pub(all) struct Nutrition {
kcal : Int
protein_g : Int
fat_g : Int
carb_g : Int
fiber_g : Int
sodium_mg : Int
} derive(Eq, Debug)
///|
pub fn Nutrition::Nutrition(
kcal? : Int = 0,
protein_g? : Int = 0,
fat_g? : Int = 0,
carb_g? : Int = 0,
fiber_g? : Int = 0,
sodium_mg? : Int = 0,
) -> Nutrition {
{ kcal, protein_g, fat_g, carb_g, fiber_g, sodium_mg }
}
///|
pub(all) struct Ingredient {
name : String
group : String
quantity : Quantity
nutrition : Nutrition
cost_cents : Int
allergens : Array[String]
tags : Array[String]
} derive(Eq, Debug)
///|
pub fn Ingredient::Ingredient(
name? : String = "",
group? : String = "",
quantity? : Quantity = Quantity(),
nutrition? : Nutrition = Nutrition(),
cost_cents? : Int = 0,
allergens? : Array[String] = [],
tags? : Array[String] = [],
) -> Ingredient {
{ name, group, quantity, nutrition, cost_cents, allergens, tags }
}
///|
pub(all) struct Recipe {
title : String
servings : Int
ingredients : Array[Ingredient]
steps : Array[String]
notes : Array[String]
} derive(Eq, Debug)
///|
pub fn Recipe::Recipe(
title? : String = "",
servings? : Int = 1,
ingredients? : Array[Ingredient] = [],
steps? : Array[String] = [],
notes? : Array[String] = [],
) -> Recipe {
{ title, servings, ingredients, steps, notes }
}
///|
pub(all) struct PantryItem {
name : String
quantity : Quantity
expires_in_days : Int
opened : Bool
preferred : Bool
} derive(Eq, Debug)
///|
pub fn PantryItem::PantryItem(
name? : String = "",
quantity? : Quantity = Quantity(),
expires_in_days? : Int = 30,
opened? : Bool = false,
preferred? : Bool = false,
) -> PantryItem {
{ name, quantity, expires_in_days, opened, preferred }
}
///|
pub(all) struct DietProfile {
avoid_allergens : Array[String]
prefer_tags : Array[String]
dislike_tags : Array[String]
max_total_cost_cents : Int
max_total_kcal : Int
min_total_protein_g : Int
} derive(Eq, Debug)
///|
pub fn DietProfile::DietProfile(
avoid_allergens? : Array[String] = [],
prefer_tags? : Array[String] = [],
dislike_tags? : Array[String] = [],
max_total_cost_cents? : Int = 0,
max_total_kcal? : Int = 0,
min_total_protein_g? : Int = 0,
) -> DietProfile {
{
avoid_allergens,
prefer_tags,
dislike_tags,
max_total_cost_cents,
max_total_kcal,
min_total_protein_g,
}
}
///|
pub(all) struct Substitution {
source : String
target : Ingredient
ratio_percent : Int
reason : String
removes_allergens : Array[String]
adds_tags : Array[String]
} derive(Eq, Debug)
///|
pub fn Substitution::Substitution(
source? : String = "",
target? : Ingredient = Ingredient(),
ratio_percent? : Int = 100,
reason? : String = "",
removes_allergens? : Array[String] = [],
adds_tags? : Array[String] = [],
) -> Substitution {
{ source, target, ratio_percent, reason, removes_allergens, adds_tags }
}
///|
pub(all) struct ShoppingLine {
name : String
needed : Quantity
available : Quantity
buy : Quantity
estimated_cost_cents : Int
} derive(Eq, Debug)
///|
pub fn ShoppingLine::ShoppingLine(
name? : String = "",
needed? : Quantity = Quantity(),
available? : Quantity = Quantity(),
buy? : Quantity = Quantity(),
estimated_cost_cents? : Int = 0,
) -> ShoppingLine {
{ name, needed, available, buy, estimated_cost_cents }
}
///|
pub(all) struct PantryWarning {
code : String
message : String
severity : String
} derive(Eq, Debug)
///|
pub fn PantryWarning::PantryWarning(
code? : String = "",
message? : String = "",
severity? : String = "info",
) -> PantryWarning {
{ code, message, severity }
}
///|
pub(all) struct PantryPlan {
verdict : String
recipe_title : String
servings : Int
total_cost_cents : Int
total_kcal : Int
total_protein_g : Int
allergen_hit_count : Int
missing_count : Int
substitution_count : Int
shopping : Array[ShoppingLine]
substitutions : Array[Substitution]
warnings : Array[PantryWarning]
} derive(Eq, Debug)
///|
pub fn PantryPlan::PantryPlan(
verdict? : String = "",
recipe_title? : String = "",
servings? : Int = 1,
total_cost_cents? : Int = 0,
total_kcal? : Int = 0,
total_protein_g? : Int = 0,
allergen_hit_count? : Int = 0,
missing_count? : Int = 0,
substitution_count? : Int = 0,
shopping? : Array[ShoppingLine] = [],
substitutions? : Array[Substitution] = [],
warnings? : Array[PantryWarning] = [],
) -> PantryPlan {
{
verdict,
recipe_title,
servings,
total_cost_cents,
total_kcal,
total_protein_g,
allergen_hit_count,
missing_count,
substitution_count,
shopping,
substitutions,
warnings,
}
}
///|
fn lower(text : String) -> String {
text.to_lower()
}
///|
fn same_name(a : String, b : String) -> Bool {
lower(a.trim().to_owned()) == lower(b.trim().to_owned())
}
///|
fn text_in(values : Array[String], target : String) -> Bool {
for value in values {
if same_name(value, target) {
return true
}
}
false
}
///|
fn count_intersection(left : Array[String], right : Array[String]) -> Int {
let mut count = 0
for item in left {
if text_in(right, item) {
count += 1
}
}
count
}
///|
fn join_text(values : Array[String]) -> String {
let out = StringBuilder()
for i in 0.. 0 {
out.write_string(", ")
}
out.write_string(values[i])
}
out.to_string()
}
///|
fn money_text(cents : Int) -> String {
let yuan = cents / 100
let fen = cents % 100
if fen < 10 {
yuan.to_string() + ".0" + fen.to_string()
} else {
yuan.to_string() + "." + fen.to_string()
}
}