use std::fmt::Display;
use bincode::{Decode, Encode};
#[derive(Clone, Encode, Decode)]
pub struct PortionsState {
pub solutions: Vec<Solution>,
pub media_input: MediaPrepInput,
pub media_result: MediaPrep,
}
impl Default for PortionsState {
fn default() -> Self {
let media_input = MediaPrepInput::default();
let media_result = media_prep(&media_input);
let mut result = Self {
solutions: Vec::new(),
media_input,
media_result,
};
result
}
}
#[derive(Default, Clone, Encode, Decode)]
pub struct Solution {
pub name: String,
pub total_volume: f32,
pub reagents: Vec<Reagent>,
pub sub_solns: Vec<Solution>,
pub reagents_sub_solns: Vec<Reagent>,
}
impl Solution {
pub fn calc_amounts(&mut self) {
self.reagents_sub_solns = Vec::new();
for sub_sol in &self.sub_solns {
for reagent in &sub_sol.reagents {
self.reagents_sub_solns.push(reagent.clone());
}
}
for reagent in &mut self.reagents {
reagent.calc_amount(self.total_volume);
}
}
}
#[derive(Clone, Copy, Encode, Decode)]
pub enum AmountCalculated {
Mass(f32),
Volume(f32),
}
impl Display for AmountCalculated {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
Self::Mass(v) => {
if *v > 1. {
format!("{:.2} g", v)
} else if *v >= 0.001 {
format!("{:.2} mg", v * 1_000.)
} else {
format!("{:.2} μg", v * 1_000_000.)
}
}
Self::Volume(v) => {
if *v > 1. {
format!("{:.2} L", v)
} else if *v >= 0.001 {
format!("{:.2} mL", v * 1_000.)
} else {
format!("{:.2} μL", v * 1_000_000.)
}
}
};
write!(f, "{}", str)
}
}
#[derive(Clone, Copy, PartialEq, Encode, Decode)]
pub enum ReagentPrep {
Mass,
Volume(f32),
}
impl Display for ReagentPrep {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
Self::Mass => "Mass".to_owned(),
Self::Volume(_molarity) => "Volume".to_owned(),
};
write!(f, "{}", str)
}
}
#[derive(Clone, Encode, Decode)]
pub struct Reagent {
pub type_: ReagentType,
pub prep: ReagentPrep,
pub molarity: f32,
pub amount_calc: AmountCalculated,
}
impl Default for Reagent {
fn default() -> Self {
Self {
type_: ReagentType::Custom(0.),
prep: ReagentPrep::Mass,
molarity: 0.,
amount_calc: AmountCalculated::Mass(0.),
}
}
}
impl Reagent {
pub fn calc_amount(&mut self, total_volume: f32) {
let moles_req = self.molarity * total_volume;
self.amount_calc = match self.prep {
ReagentPrep::Mass => AmountCalculated::Mass(self.type_.weight() * moles_req),
ReagentPrep::Volume(reagent_molarity) => {
if reagent_molarity.abs() < 0.00000001 {
AmountCalculated::Volume(0.)
} else {
AmountCalculated::Volume(moles_req / reagent_molarity)
}
}
};
}
}
#[derive(Clone, Copy, PartialEq, Encode, Decode)]
pub enum ReagentType {
Custom(f32), Solution(usize),
SodiumChloride,
SodiumPhosphateMonobasic,
SodiumPhosphateDibasic,
SodiumPhosphateDibasicHeptahydrate,
PotassiumPhosphateMonobasic,
PotassiumPhosphateDibasic,
TrisHcl,
Iptg,
Imidazole,
Lysozyme,
Mes,
Bes,
Tes,
CitricAcid,
Edta,
HydrochloricAcid,
SodiumHydroxide,
BromophenolBlue,
Dtt,
MagnesiumChloride,
Glycine,
Sds,
Tris,
}
impl ReagentType {
pub fn weight(&self) -> f32 {
match self {
Self::Custom(weight) => *weight,
Self::Solution(_) => 0., Self::SodiumChloride => 58.44,
Self::SodiumPhosphateMonobasic => 119.98,
Self::SodiumPhosphateDibasic => 141.96,
Self::SodiumPhosphateDibasicHeptahydrate => 268.10,
Self::PotassiumPhosphateMonobasic => 136.08,
Self::PotassiumPhosphateDibasic => 174.17,
Self::TrisHcl => 121.14,
Self::Iptg => 238.298,
Self::Imidazole => 68.08,
Self::Lysozyme => 14_388.,
Self::Mes => 195.24,
Self::Bes => 213.25,
Self::Tes => 229.25,
Self::CitricAcid => 192.12,
Self::Edta => 292.24,
Self::HydrochloricAcid => 36.46,
Self::SodiumHydroxide => 40.,
Self::BromophenolBlue => 669.96,
Self::Dtt => 154.25,
Self::MagnesiumChloride => 95.211,
Self::Glycine => 75.07,
Self::Sds => 288.5,
Self::Tris => 121.14,
}
}
}
impl Display for ReagentType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
Self::Custom(_) => "Custom".to_owned(),
Self::Solution(_) => "Solution".to_owned(), Self::SodiumChloride => "NaCl".to_owned(),
Self::SodiumPhosphateMonobasic => "NaH₂PO₄ (Mono)".to_owned(),
Self::SodiumPhosphateDibasic => "Na₂HPO₄ (Di)".to_owned(),
Self::SodiumPhosphateDibasicHeptahydrate => "Na₂HPO₄·7H₂O".to_owned(),
Self::PotassiumPhosphateMonobasic => "H₂KO₄P".to_owned(),
Self::PotassiumPhosphateDibasic => "HK₂O₄P".to_owned(),
Self::TrisHcl => "Tris-HCl".to_owned(),
Self::Iptg => "IPTG".to_owned(),
Self::Imidazole => "Imidazole".to_owned(),
Self::Lysozyme => "Lysozyme".to_owned(),
Self::Mes => "MES".to_owned(),
Self::Bes => "BES".to_owned(),
Self::Tes => "TES".to_owned(),
Self::CitricAcid => "Citric acid".to_owned(),
Self::Edta => "EDTA".to_owned(),
Self::HydrochloricAcid => "HCl".to_owned(),
Self::SodiumHydroxide => "NaOH".to_owned(),
Self::BromophenolBlue => "Bromophenol blue".to_owned(),
Self::Dtt => "DTT".to_owned(),
Self::MagnesiumChloride => "MgCl₂".to_owned(),
Self::Sds => "SDS".to_owned(),
Self::Glycine => "Glycine".to_owned(),
Self::Tris => "Tris".to_owned(),
};
write!(f, "{}", str)
}
}
#[derive(Clone, Copy, PartialEq, Encode, Decode)]
pub enum PlateSize {
D60,
D90,
D100,
D150,
}
impl PlateSize {
pub fn volume(&self) -> f32 {
match self {
Self::D60 => 0.007,
Self::D90 => 0.01575,
Self::D100 => 0.01944,
Self::D150 => 0.04375,
}
}
}
impl Display for PlateSize {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
Self::D60 => "60mm",
Self::D90 => "90mm",
Self::D100 => "100mm",
Self::D150 => "150mm",
};
write!(f, "{}", str)
}
}
#[derive(Clone, PartialEq, Encode, Decode)]
pub enum MediaPrepInput {
Plates((PlateSize, usize)), Liquid(f32), }
impl Display for MediaPrepInput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
Self::Plates(_) => "Plates",
Self::Liquid(_) => "Liquid culture",
};
write!(f, "{}", str)
}
}
impl Default for MediaPrepInput {
fn default() -> Self {
Self::Plates((PlateSize::D90, 6))
}
}
#[derive(Clone, Default, Encode, Decode, Debug)]
pub struct MediaPrep {
pub water: f32, pub food: f32, pub agar: f32, pub antibiotic: f32, }
pub fn media_prep(input: &MediaPrepInput) -> MediaPrep {
let (volume, agar) = match input {
MediaPrepInput::Plates((plate_size, num)) => {
let volume = plate_size.volume() * *num as f32;
(volume, volume * 15.)
}
MediaPrepInput::Liquid(volume) => (*volume, 0.),
};
MediaPrep {
water: volume,
food: volume * 25.,
agar,
antibiotic: volume,
}
}