use std::fmt;
use crate::{Product, Reactant, ReactionArrow, ReactionValidationError};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ReactionEquation {
reactants: Vec<Reactant>,
products: Vec<Product>,
arrow: ReactionArrow,
}
impl ReactionEquation {
#[must_use]
pub const fn new() -> Self {
Self {
reactants: Vec::new(),
products: Vec::new(),
arrow: ReactionArrow::Forward,
}
}
#[must_use]
pub fn with_reactant<T>(mut self, reactant: T) -> Self
where
T: Into<Reactant>,
{
self.reactants.push(reactant.into());
self
}
#[must_use]
pub fn with_product<T>(mut self, product: T) -> Self
where
T: Into<Product>,
{
self.products.push(product.into());
self
}
#[must_use]
pub const fn with_arrow(mut self, arrow: ReactionArrow) -> Self {
self.arrow = arrow;
self
}
#[must_use]
pub fn reactants(&self) -> &[Reactant] {
&self.reactants
}
#[must_use]
pub fn products(&self) -> &[Product] {
&self.products
}
#[must_use]
pub const fn arrow(&self) -> ReactionArrow {
self.arrow
}
pub fn validate(&self) -> Result<(), ReactionValidationError> {
match (self.reactants.is_empty(), self.products.is_empty()) {
(true, true) => Err(ReactionValidationError::EmptyReaction),
(true, false) => Err(ReactionValidationError::MissingReactants),
(false, true) => Err(ReactionValidationError::MissingProducts),
(false, false) => Ok(()),
}
}
}
impl Default for ReactionEquation {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for ReactionEquation {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write_side(formatter, &self.reactants)?;
write!(formatter, " {} ", self.arrow)?;
write_side(formatter, &self.products)
}
}
fn write_side<T>(formatter: &mut fmt::Formatter<'_>, terms: &[T]) -> fmt::Result
where
T: fmt::Display,
{
for (index, term) in terms.iter().enumerate() {
if index > 0 {
formatter.write_str(" + ")?;
}
write!(formatter, "{term}")?;
}
Ok(())
}