use approx::AbsDiffEq;
use serde::{Deserialize, Serialize};
use struct_iterable::Iterable;
use crate::{
composition::{ScaleComponents, SolidsBreakdown},
util::{iter_all_abs_diff_eq, iter_fields_as},
};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Iterable, PartialEq, Serialize, Deserialize, Copy, Clone, Debug)]
#[serde(default, deny_unknown_fields)]
pub struct Solids {
pub milk: SolidsBreakdown,
pub egg: SolidsBreakdown,
pub cocoa: SolidsBreakdown,
pub nut: SolidsBreakdown,
pub other: SolidsBreakdown,
}
impl Solids {
#[must_use]
pub const fn empty() -> Self {
Self {
milk: SolidsBreakdown::empty(),
egg: SolidsBreakdown::empty(),
cocoa: SolidsBreakdown::empty(),
nut: SolidsBreakdown::empty(),
other: SolidsBreakdown::empty(),
}
}
#[must_use]
pub const fn new() -> Self {
Self::empty()
}
#[must_use]
pub const fn milk(self, milk: SolidsBreakdown) -> Self {
Self { milk, ..self }
}
#[must_use]
pub const fn egg(self, egg: SolidsBreakdown) -> Self {
Self { egg, ..self }
}
#[must_use]
pub const fn cocoa(self, cocoa: SolidsBreakdown) -> Self {
Self { cocoa, ..self }
}
#[must_use]
pub const fn nut(self, nut: SolidsBreakdown) -> Self {
Self { nut, ..self }
}
#[must_use]
pub const fn other(self, other: SolidsBreakdown) -> Self {
Self { other, ..self }
}
}
#[cfg_attr(feature = "wasm", wasm_bindgen)]
impl Solids {
#[allow(clippy::missing_const_for_fn)] #[cfg_attr(coverage, coverage(off))]
#[cfg(feature = "wasm")]
#[wasm_bindgen(constructor)]
#[must_use]
pub fn new_wasm() -> Self {
Self::new()
}
fn iter_fields_as_solids_breakdown(&self) -> impl Iterator<Item = &SolidsBreakdown> {
iter_fields_as::<SolidsBreakdown, _>(self)
}
fn sum_solid_breakdowns_field(&self, f: fn(&SolidsBreakdown) -> f64) -> f64 {
self.iter_fields_as_solids_breakdown().map(f).sum::<f64>()
}
#[must_use]
pub fn total(&self) -> f64 {
self.sum_solid_breakdowns_field(SolidsBreakdown::total)
}
#[must_use]
pub fn all(&self) -> SolidsBreakdown {
self.iter_fields_as_solids_breakdown()
.fold(SolidsBreakdown::empty(), |acc, b| acc.add(b))
}
}
impl ScaleComponents for Solids {
fn scale(&self, factor: f64) -> Self {
Self {
milk: self.milk.scale(factor),
egg: self.egg.scale(factor),
cocoa: self.cocoa.scale(factor),
nut: self.nut.scale(factor),
other: self.other.scale(factor),
}
}
fn add(&self, other: &Self) -> Self {
Self {
milk: self.milk.add(&other.milk),
egg: self.egg.add(&other.egg),
cocoa: self.cocoa.add(&other.cocoa),
nut: self.nut.add(&other.nut),
other: self.other.add(&other.other),
}
}
}
impl AbsDiffEq for Solids {
type Epsilon = f64;
fn default_epsilon() -> Self::Epsilon {
f64::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
iter_all_abs_diff_eq::<f64, SolidsBreakdown, Self>(self, other, epsilon)
}
}
impl Default for Solids {
fn default() -> Self {
Self::empty()
}
}