use approx::AbsDiffEq;
use serde::{Deserialize, Serialize};
use struct_iterable::Iterable;
use crate::{
composition::ScaleComponents,
constants,
error::{Error, Result},
util::{iter_all_abs_diff_eq, iter_fields_as},
};
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
#[cfg(doc)]
use crate::composition::Polyols;
#[doc = include_str!("../../docs/bibs/9.md")]
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[derive(Iterable, PartialEq, Serialize, Deserialize, Copy, Clone, Debug)]
#[serde(default, deny_unknown_fields)]
pub struct ArtificialSweeteners {
#[doc = include_str!("../../docs/bibs/9.md")]
#[doc = include_str!("../../docs/bibs/10.md")]
#[doc = include_str!("../../docs/bibs/21.md")]
pub aspartame: f64,
#[doc = include_str!("../../docs/bibs/9.md")]
#[doc = include_str!("../../docs/bibs/10.md")]
#[doc = include_str!("../../docs/bibs/32.md")]
pub cyclamate: f64,
#[doc = include_str!("../../docs/bibs/9.md")]
#[doc = include_str!("../../docs/bibs/10.md")]
#[doc = include_str!("../../docs/bibs/22.md")]
pub saccharin: f64,
#[doc = include_str!("../../docs/bibs/9.md")]
#[doc = include_str!("../../docs/bibs/10.md")]
#[doc = include_str!("../../docs/bibs/11.md")]
#[doc = include_str!("../../docs/bibs/15.md")]
#[doc = include_str!("../../docs/bibs/23.md")]
pub sucralose: f64,
#[doc = include_str!("../../docs/bibs/9.md")]
#[doc = include_str!("../../docs/bibs/28.md")]
pub steviosides: f64,
#[doc = include_str!("../../docs/bibs/9.md")]
#[doc = include_str!("../../docs/bibs/29.md")]
pub mogrosides: f64,
pub other: f64,
}
impl ArtificialSweeteners {
#[must_use]
pub const fn empty() -> Self {
Self {
aspartame: 0.0,
cyclamate: 0.0,
saccharin: 0.0,
sucralose: 0.0,
steviosides: 0.0,
mogrosides: 0.0,
other: 0.0,
}
}
#[must_use]
pub const fn new() -> Self {
Self::empty()
}
#[must_use]
pub const fn aspartame(self, aspartame: f64) -> Self {
Self { aspartame, ..self }
}
#[must_use]
pub const fn cyclamate(self, cyclamate: f64) -> Self {
Self { cyclamate, ..self }
}
#[must_use]
pub const fn saccharin(self, saccharin: f64) -> Self {
Self { saccharin, ..self }
}
#[must_use]
pub const fn sucralose(self, sucralose: f64) -> Self {
Self { sucralose, ..self }
}
#[must_use]
pub const fn steviosides(self, steviosides: f64) -> Self {
Self { steviosides, ..self }
}
#[must_use]
pub const fn mogrosides(self, mogrosides: f64) -> Self {
Self { mogrosides, ..self }
}
#[must_use]
pub const fn other(self, other: f64) -> Self {
Self { other, ..self }
}
#[must_use]
pub fn total(&self) -> f64 {
iter_fields_as::<f64, _>(self).sum()
}
pub fn energy(&self) -> Result<f64> {
if self.other != 0.0 {
return Err(Error::CannotComputeEnergy(
"Cannot compute energy with other artificial sweeteners".to_string(),
));
}
Ok([
self.aspartame * constants::energy::ASPARTAME,
self.cyclamate * constants::energy::CYCLAMATE,
self.saccharin * constants::energy::SACCHARIN,
self.sucralose * constants::energy::SUCRALOSE,
self.steviosides * constants::energy::STEVIOSIDES,
self.mogrosides * constants::energy::MOGROSIDES,
]
.into_iter()
.sum::<f64>()
/ 100.0)
}
pub fn to_pod(&self) -> Result<f64> {
if self.other != 0.0 {
return Err(Error::CannotComputePOD("Cannot compute POD with other artificial sweeteners".to_string()));
}
Ok([
self.aspartame * constants::pod::ASPARTAME,
self.cyclamate * constants::pod::CYCLAMATE,
self.saccharin * constants::pod::SACCHARIN,
self.sucralose * constants::pod::SUCRALOSE,
self.steviosides * constants::pod::STEVIOSIDES,
self.mogrosides * constants::pod::MOGROSIDES,
]
.into_iter()
.sum::<f64>()
/ 100.0)
}
pub fn to_pac(&self) -> Result<f64> {
if self.other != 0.0 {
return Err(Error::CannotComputePAC("Cannot compute PAC with other artificial sweeteners".to_string()));
}
Ok([
self.aspartame * constants::pac::ASPARTAME,
self.cyclamate * constants::pac::CYCLAMATE,
self.saccharin * constants::pac::SACCHARIN,
self.sucralose * constants::pac::SUCRALOSE,
]
.into_iter()
.sum::<f64>()
/ 100.0)
}
}
#[cfg_attr(coverage, coverage(off))]
#[cfg(feature = "wasm")]
#[wasm_bindgen]
impl ArtificialSweeteners {
#[allow(clippy::missing_const_for_fn)] #[wasm_bindgen(constructor)]
#[must_use]
pub fn new_wasm() -> Self {
Self::new()
}
}
impl ScaleComponents for ArtificialSweeteners {
fn scale(&self, factor: f64) -> Self {
Self {
aspartame: self.aspartame * factor,
cyclamate: self.cyclamate * factor,
saccharin: self.saccharin * factor,
sucralose: self.sucralose * factor,
steviosides: self.steviosides * factor,
mogrosides: self.mogrosides * factor,
other: self.other * factor,
}
}
fn add(&self, other: &Self) -> Self {
Self {
aspartame: self.aspartame + other.aspartame,
cyclamate: self.cyclamate + other.cyclamate,
saccharin: self.saccharin + other.saccharin,
sucralose: self.sucralose + other.sucralose,
steviosides: self.steviosides + other.steviosides,
mogrosides: self.mogrosides + other.mogrosides,
other: self.other + other.other,
}
}
}
impl AbsDiffEq for ArtificialSweeteners {
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, f64, Self>(self, other, epsilon)
}
}
impl Default for ArtificialSweeteners {
fn default() -> Self {
Self::empty()
}
}