Skip to main content

use_reaction/
chemical_reaction.rs

1use std::fmt;
2
3use crate::{
4    Product, Reactant, ReactionArrow, ReactionCondition, ReactionConditionSet, ReactionEquation,
5    ReactionKind, ReactionValidationError,
6};
7
8/// A chemical reaction with an equation, optional conditions, and classification labels.
9#[derive(Clone, Debug, Eq, PartialEq)]
10pub struct ChemicalReaction {
11    equation: ReactionEquation,
12    conditions: ReactionConditionSet,
13    kinds: Vec<ReactionKind>,
14}
15
16impl ChemicalReaction {
17    /// Creates an empty reaction builder/value.
18    #[must_use]
19    pub const fn new() -> Self {
20        Self {
21            equation: ReactionEquation::new(),
22            conditions: ReactionConditionSet::new(),
23            kinds: Vec::new(),
24        }
25    }
26
27    /// Adds a reactant and returns the updated reaction.
28    #[must_use]
29    pub fn with_reactant<T>(mut self, reactant: T) -> Self
30    where
31        T: Into<Reactant>,
32    {
33        self.equation = self.equation.with_reactant(reactant);
34        self
35    }
36
37    /// Adds a product and returns the updated reaction.
38    #[must_use]
39    pub fn with_product<T>(mut self, product: T) -> Self
40    where
41        T: Into<Product>,
42    {
43        self.equation = self.equation.with_product(product);
44        self
45    }
46
47    /// Sets the reaction arrow.
48    #[must_use]
49    pub fn with_arrow(mut self, arrow: ReactionArrow) -> Self {
50        self.equation = self.equation.with_arrow(arrow);
51        self
52    }
53
54    /// Adds a condition and returns the updated reaction.
55    #[must_use]
56    pub fn with_condition<T>(mut self, condition: T) -> Self
57    where
58        T: Into<ReactionCondition>,
59    {
60        self.conditions.push(condition);
61        self
62    }
63
64    /// Adds a kind label if it is not already present.
65    #[must_use]
66    pub fn with_kind(mut self, kind: ReactionKind) -> Self {
67        if !self.kinds.contains(&kind) {
68            self.kinds.push(kind);
69        }
70        self
71    }
72
73    /// Returns the reaction equation.
74    #[must_use]
75    pub const fn equation(&self) -> &ReactionEquation {
76        &self.equation
77    }
78
79    /// Returns reactants in insertion order.
80    #[must_use]
81    pub fn reactants(&self) -> &[Reactant] {
82        self.equation.reactants()
83    }
84
85    /// Returns products in insertion order.
86    #[must_use]
87    pub fn products(&self) -> &[Product] {
88        self.equation.products()
89    }
90
91    /// Returns the reaction arrow.
92    #[must_use]
93    pub const fn arrow(&self) -> ReactionArrow {
94        self.equation.arrow()
95    }
96
97    /// Returns the reaction conditions.
98    #[must_use]
99    pub const fn conditions(&self) -> &ReactionConditionSet {
100        &self.conditions
101    }
102
103    /// Returns reaction kind labels in insertion order.
104    #[must_use]
105    pub fn kinds(&self) -> &[ReactionKind] {
106        &self.kinds
107    }
108
109    /// Validates the equation and condition descriptors.
110    ///
111    /// # Errors
112    ///
113    /// Returns a [`ReactionValidationError`] when the equation is incomplete or a condition label
114    /// is empty.
115    pub fn validate(&self) -> Result<(), ReactionValidationError> {
116        self.equation.validate()?;
117        self.conditions.validate()
118    }
119}
120
121impl Default for ChemicalReaction {
122    fn default() -> Self {
123        Self::new()
124    }
125}
126
127impl fmt::Display for ChemicalReaction {
128    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
129        write!(formatter, "{}", self.equation)
130    }
131}