calories/shared/src/structs/nutrition.rs
2025-03-27 17:59:42 +01:00

177 lines
4.9 KiB
Rust

cfg_if::cfg_if! {
if #[cfg(feature = "sql")] {
use mysql::prelude::*;
use mysql::*;
use mysql_common::frunk::{hlist_pat, HList};
use std::collections::HashMap;
use struct_field_names_as_array::FieldNamesAsArray;
} else {}
}
use serde::{Deserialize, Serialize};
#[allow(non_snake_case)]
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "sql", derive(FieldNamesAsArray))]
pub struct Nutrition {
pub id: u32,
pub name: String,
pub manufacturer: String,
pub barcode: String,
pub amount: u32,
pub divisor: u32,
pub kJ: f32,
pub kcal: f32,
pub fat: f32,
pub saturated_fat: f32,
pub carbohydrate: f32,
pub sugar: f32,
pub fibres: f32,
pub protein: f32,
pub salt: f32,
pub vitamin_b2: f32,
pub vitamin_b12: f32,
pub calcium: f32,
pub phosphor: f32,
pub vitamin_d: f32,
}
#[cfg(feature = "sql")]
type RowType = HList!(
u32,
Option<String>,
Option<String>,
Option<String>,
Option<u32>,
Option<u32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
Option<f32>,
);
impl Nutrition {
#[cfg(feature = "sql")]
fn get_sql_fields() -> String {
Nutrition::FIELD_NAMES_AS_ARRAY
.iter()
.cloned()
.intersperse(", ")
.collect()
}
#[cfg(feature = "sql")]
fn query_map_helper() -> (String, impl Fn(RowType) -> Nutrition) {
let sql_query = format!("SELECT {} from nutrition", Self::get_sql_fields());
let construction_closure = |row: RowType| {
let hlist_pat![
id,
name,
manufacturer,
barcode,
amount,
divisor,
k_j,
kcal,
fat,
saturated_fat,
carbohydrate,
sugar,
fibres,
protein,
salt,
vitamin_b2,
vitamin_b12,
calcium,
phosphor,
vitamin_d,
] = row;
Nutrition {
id,
name: name.unwrap_or("".to_string()),
manufacturer: manufacturer.unwrap_or("".to_string()),
barcode: barcode.unwrap_or("".to_string()),
amount: amount.unwrap_or(0),
divisor: divisor.unwrap_or(0),
kJ: k_j.unwrap_or(0.),
kcal: kcal.unwrap_or(0.),
fat: fat.unwrap_or(0.),
saturated_fat: saturated_fat.unwrap_or(0.),
carbohydrate: carbohydrate.unwrap_or(0.),
sugar: sugar.unwrap_or(0.),
fibres: fibres.unwrap_or(0.),
protein: protein.unwrap_or(0.),
salt: salt.unwrap_or(0.),
vitamin_b2: vitamin_b2.unwrap_or(0.),
vitamin_b12: vitamin_b12.unwrap_or(0.),
calcium: calcium.unwrap_or(0.),
phosphor: phosphor.unwrap_or(0.),
vitamin_d: vitamin_d.unwrap_or(0.),
}
};
(sql_query, construction_closure)
}
#[cfg(feature = "sql")]
pub async fn get_nutrition_rows(sql_pool: Pool) -> Result<Vec<Nutrition>> {
let mut conn = sql_pool
.get_conn()
.expect("Cannot establish database connection");
let (nutrition_query, nutrition_closure) = Self::query_map_helper();
let nutrition_val: Vec<Nutrition> = conn
.query_map(nutrition_query, nutrition_closure)
.expect("Data in database doesn't match the Nutrition class");
Ok(nutrition_val)
}
#[cfg(feature = "sql")]
pub async fn get_nutrition_hashmap(sql_pool: Pool) -> Result<HashMap<u32, Nutrition>> {
let nutrition_vec = Self::get_nutrition_rows(sql_pool).await?;
let nutrition_hashmap: HashMap<u32, Nutrition> =
nutrition_vec.into_iter().map(|n| (n.id, n)).collect();
Ok(nutrition_hashmap)
}
pub fn total_kcal(&self) -> f32 {
match self.divisor {
0 => 0.,
_ => self.kcal * self.amount as f32 / self.divisor as f32,
}
}
pub fn is_valid(&self) -> bool {
// Comparing self.kcal with 0. should be okay, since it is initialized to 0. and if it is
// changed in any way, this function should return true
self.amount != 0 && self.divisor != 0 && self.kcal != 0.
}
pub fn id(&self) -> u32 {
self.id
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn manufacturer(&self) -> String {
self.manufacturer.clone()
}
pub fn barcode(&self) -> String {
self.barcode.clone()
}
}