use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum Domain {
Bacteria,
Archaea,
Eukarya,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GramStain {
Positive,
Negative,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum CellShape {
Coccus,
Bacillus,
Spirillum,
Vibrio,
Spirochete,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum OxygenRequirement {
ObligateAerobe,
ObligateAnaerobe,
Facultative,
Microaerophilic,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MicrobialProfile {
pub domain: Domain,
pub gram_stain: GramStain,
pub shape: CellShape,
pub oxygen: OxygenRequirement,
}
impl Domain {
#[inline]
#[must_use]
pub const fn is_eukaryotic(self) -> bool {
matches!(self, Self::Eukarya)
}
#[inline]
#[must_use]
pub const fn is_prokaryotic(self) -> bool {
matches!(self, Self::Bacteria | Self::Archaea)
}
}
impl GramStain {
#[inline]
#[must_use]
pub const fn has_outer_membrane(self) -> bool {
matches!(self, Self::Negative)
}
}
impl CellShape {
#[inline]
#[must_use]
pub const fn is_elongated(self) -> bool {
matches!(
self,
Self::Bacillus | Self::Spirillum | Self::Vibrio | Self::Spirochete
)
}
}
impl OxygenRequirement {
#[inline]
#[must_use]
pub const fn tolerates_oxygen(self) -> bool {
matches!(
self,
Self::ObligateAerobe | Self::Facultative | Self::Microaerophilic
)
}
#[inline]
#[must_use]
pub const fn tolerates_anaerobic(self) -> bool {
matches!(self, Self::ObligateAnaerobe | Self::Facultative)
}
}
impl MicrobialProfile {
#[inline]
#[must_use]
pub fn beta_lactam_susceptible(&self) -> bool {
matches!(self.domain, Domain::Bacteria) && matches!(self.gram_stain, GramStain::Positive)
}
#[inline]
#[must_use]
pub fn can_form_endospores(&self) -> bool {
matches!(self.domain, Domain::Bacteria)
&& matches!(self.gram_stain, GramStain::Positive)
&& matches!(self.shape, CellShape::Bacillus)
}
#[inline]
#[must_use]
pub fn likely_motile(&self) -> bool {
matches!(
self.shape,
CellShape::Spirillum | CellShape::Vibrio | CellShape::Spirochete
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_domain_serde_roundtrip() {
let d = Domain::Bacteria;
let json = serde_json::to_string(&d).unwrap();
let back: Domain = serde_json::from_str(&json).unwrap();
assert_eq!(d, back);
}
#[test]
fn test_gram_stain_serde_roundtrip() {
let gs = GramStain::Negative;
let json = serde_json::to_string(&gs).unwrap();
let back: GramStain = serde_json::from_str(&json).unwrap();
assert_eq!(gs, back);
}
#[test]
fn test_cell_shape_serde_roundtrip() {
let shape = CellShape::Spirochete;
let json = serde_json::to_string(&shape).unwrap();
let back: CellShape = serde_json::from_str(&json).unwrap();
assert_eq!(shape, back);
}
#[test]
fn test_oxygen_requirement_serde_roundtrip() {
let oxy = OxygenRequirement::Facultative;
let json = serde_json::to_string(&oxy).unwrap();
let back: OxygenRequirement = serde_json::from_str(&json).unwrap();
assert_eq!(oxy, back);
}
#[test]
fn test_domain_eukaryotic() {
assert!(Domain::Eukarya.is_eukaryotic());
assert!(!Domain::Bacteria.is_eukaryotic());
assert!(!Domain::Archaea.is_eukaryotic());
}
#[test]
fn test_domain_prokaryotic() {
assert!(Domain::Bacteria.is_prokaryotic());
assert!(Domain::Archaea.is_prokaryotic());
assert!(!Domain::Eukarya.is_prokaryotic());
}
#[test]
fn test_gram_stain_outer_membrane() {
assert!(GramStain::Negative.has_outer_membrane());
assert!(!GramStain::Positive.has_outer_membrane());
}
#[test]
fn test_cell_shape_elongated() {
assert!(CellShape::Bacillus.is_elongated());
assert!(CellShape::Spirillum.is_elongated());
assert!(!CellShape::Coccus.is_elongated());
}
#[test]
fn test_oxygen_tolerates() {
assert!(OxygenRequirement::Facultative.tolerates_oxygen());
assert!(OxygenRequirement::Facultative.tolerates_anaerobic());
assert!(!OxygenRequirement::ObligateAerobe.tolerates_anaerobic());
assert!(!OxygenRequirement::ObligateAnaerobe.tolerates_oxygen());
}
#[test]
fn test_beta_lactam_susceptible() {
let gram_pos = MicrobialProfile {
domain: Domain::Bacteria,
gram_stain: GramStain::Positive,
shape: CellShape::Coccus,
oxygen: OxygenRequirement::Facultative,
};
assert!(gram_pos.beta_lactam_susceptible());
let gram_neg = MicrobialProfile {
domain: Domain::Bacteria,
gram_stain: GramStain::Negative,
shape: CellShape::Bacillus,
oxygen: OxygenRequirement::Facultative,
};
assert!(!gram_neg.beta_lactam_susceptible());
}
#[test]
fn test_can_form_endospores() {
let bacillus = MicrobialProfile {
domain: Domain::Bacteria,
gram_stain: GramStain::Positive,
shape: CellShape::Bacillus,
oxygen: OxygenRequirement::Facultative,
};
assert!(bacillus.can_form_endospores());
let coccus = MicrobialProfile {
domain: Domain::Bacteria,
gram_stain: GramStain::Positive,
shape: CellShape::Coccus,
oxygen: OxygenRequirement::Facultative,
};
assert!(!coccus.can_form_endospores());
}
#[test]
fn test_likely_motile() {
let spirillum = MicrobialProfile {
domain: Domain::Bacteria,
gram_stain: GramStain::Negative,
shape: CellShape::Spirillum,
oxygen: OxygenRequirement::Microaerophilic,
};
assert!(spirillum.likely_motile());
let coccus = MicrobialProfile {
domain: Domain::Bacteria,
gram_stain: GramStain::Positive,
shape: CellShape::Coccus,
oxygen: OxygenRequirement::Facultative,
};
assert!(!coccus.likely_motile());
}
#[test]
fn test_microbial_profile_serde_roundtrip() {
let profile = MicrobialProfile {
domain: Domain::Bacteria,
gram_stain: GramStain::Negative,
shape: CellShape::Bacillus,
oxygen: OxygenRequirement::Facultative,
};
let json = serde_json::to_string(&profile).unwrap();
let back: MicrobialProfile = serde_json::from_str(&json).unwrap();
assert_eq!(profile.domain, back.domain);
assert_eq!(profile.shape, back.shape);
}
}