1use std::fmt;
2
3use use_chemical_formula::ChemicalFormula;
4
5use crate::{
6 AtomConnection, AtomCount, MolecularAtom, MolecularFormula, MoleculeBuilder, MoleculeCharge,
7 MoleculeKind, MoleculeName, MoleculeValidationError,
8};
9
10#[derive(Clone, Debug, Eq, PartialEq)]
12pub struct Molecule {
13 name: MoleculeName,
14 formula: MolecularFormula,
15 atoms: Vec<MolecularAtom>,
16 connections: Vec<AtomConnection>,
17 charge: MoleculeCharge,
18 kinds: Vec<MoleculeKind>,
19}
20
21impl Molecule {
22 pub fn new(name: &str, formula: ChemicalFormula) -> Result<Self, MoleculeValidationError> {
28 Ok(Self {
29 name: MoleculeName::new(name)?,
30 formula: MolecularFormula::new(formula),
31 atoms: Vec::new(),
32 connections: Vec::new(),
33 charge: MoleculeCharge::NEUTRAL,
34 kinds: Vec::new(),
35 })
36 }
37
38 #[must_use]
40 pub fn builder(name: &str) -> MoleculeBuilder {
41 MoleculeBuilder::new(name)
42 }
43
44 #[must_use]
46 pub const fn name(&self) -> &MoleculeName {
47 &self.name
48 }
49
50 #[must_use]
52 pub fn formula(&self) -> &ChemicalFormula {
53 self.formula.as_formula()
54 }
55
56 #[must_use]
58 pub const fn molecular_formula(&self) -> &MolecularFormula {
59 &self.formula
60 }
61
62 #[must_use]
64 pub fn atoms(&self) -> &[MolecularAtom] {
65 &self.atoms
66 }
67
68 #[must_use]
70 pub fn atom_count(&self) -> usize {
71 self.atoms.len()
72 }
73
74 #[must_use]
76 pub fn atom_count_value(&self) -> AtomCount {
77 AtomCount::new(self.atom_count())
78 }
79
80 #[must_use]
82 pub fn connections(&self) -> &[AtomConnection] {
83 &self.connections
84 }
85
86 #[must_use]
88 pub const fn charge(&self) -> MoleculeCharge {
89 self.charge
90 }
91
92 #[must_use]
94 pub fn kinds(&self) -> &[MoleculeKind] {
95 &self.kinds
96 }
97
98 #[must_use]
100 pub fn with_kind(mut self, kind: MoleculeKind) -> Self {
101 if !self.kinds.contains(&kind) {
102 self.kinds.push(kind);
103 }
104 self
105 }
106
107 #[must_use]
109 pub const fn with_charge(mut self, charge: MoleculeCharge) -> Self {
110 self.charge = charge;
111 self
112 }
113
114 #[must_use]
116 pub fn with_atom(mut self, atom: MolecularAtom) -> Self {
117 self.atoms.push(atom);
118 self
119 }
120
121 pub fn try_with_connection(
128 mut self,
129 connection: AtomConnection,
130 ) -> Result<Self, MoleculeValidationError> {
131 let connection = connection.validate_indices(self.atoms.len())?;
132 if !self.connections.contains(&connection) {
133 self.connections.push(connection);
134 }
135 Ok(self)
136 }
137}
138
139impl fmt::Display for Molecule {
140 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
141 write!(formatter, "{} ({})", self.name, self.formula)
142 }
143}