1use std::fmt;
2
3use crate::{
4 BondDescriptor, BondEndpoint, BondKind, BondLength, BondOrder, BondParticipant, BondPolarity,
5 BondStrength, BondValidationError,
6};
7
8#[derive(Clone, Debug, PartialEq)]
10pub struct Bond {
11 kind: BondKind,
12 order: Option<BondOrder>,
13 endpoints: Vec<BondEndpoint>,
14 participants: Vec<BondParticipant>,
15 polarity: Option<BondPolarity>,
16 strength: Option<BondStrength>,
17 length: Option<BondLength>,
18 angle_label: Option<BondDescriptor>,
19 descriptors: Vec<BondDescriptor>,
20}
21
22impl Bond {
23 #[must_use]
25 pub fn new(kind: BondKind) -> Self {
26 Self {
27 kind,
28 order: None,
29 endpoints: Vec::new(),
30 participants: Vec::new(),
31 polarity: None,
32 strength: None,
33 length: None,
34 angle_label: None,
35 descriptors: Vec::new(),
36 }
37 }
38
39 #[must_use]
41 pub fn between(endpoint_a: BondEndpoint, endpoint_b: BondEndpoint, kind: BondKind) -> Self {
42 Self::new(kind)
43 .with_endpoint(endpoint_a)
44 .with_endpoint(endpoint_b)
45 }
46
47 #[must_use]
49 pub const fn kind(&self) -> BondKind {
50 self.kind
51 }
52
53 #[must_use]
55 pub const fn order(&self) -> Option<BondOrder> {
56 self.order
57 }
58
59 #[must_use]
61 pub fn endpoints(&self) -> &[BondEndpoint] {
62 &self.endpoints
63 }
64
65 #[must_use]
67 pub fn participants(&self) -> &[BondParticipant] {
68 &self.participants
69 }
70
71 #[must_use]
73 pub const fn polarity(&self) -> Option<BondPolarity> {
74 self.polarity
75 }
76
77 #[must_use]
79 pub const fn strength(&self) -> Option<BondStrength> {
80 self.strength
81 }
82
83 #[must_use]
85 pub const fn length(&self) -> Option<&BondLength> {
86 self.length.as_ref()
87 }
88
89 #[must_use]
91 pub const fn angle_label(&self) -> Option<&BondDescriptor> {
92 self.angle_label.as_ref()
93 }
94
95 #[must_use]
97 pub fn descriptors(&self) -> &[BondDescriptor] {
98 &self.descriptors
99 }
100
101 #[must_use]
103 pub const fn with_order(mut self, order: BondOrder) -> Self {
104 self.order = Some(order);
105 self
106 }
107
108 #[must_use]
110 pub fn with_endpoint(mut self, endpoint: BondEndpoint) -> Self {
111 self.endpoints.push(endpoint);
112 self
113 }
114
115 pub fn try_with_endpoint(self, endpoint: &str) -> Result<Self, BondValidationError> {
121 Ok(self.with_endpoint(BondEndpoint::new(endpoint)?))
122 }
123
124 #[must_use]
126 pub fn with_participant(mut self, participant: BondParticipant) -> Self {
127 if !self.participants.contains(&participant) {
128 self.participants.push(participant);
129 }
130 self
131 }
132
133 pub fn try_with_participant(self, participant: &str) -> Result<Self, BondValidationError> {
140 Ok(self.with_participant(BondParticipant::new(participant)?))
141 }
142
143 #[must_use]
145 pub const fn with_polarity(mut self, polarity: BondPolarity) -> Self {
146 self.polarity = Some(polarity);
147 self
148 }
149
150 #[must_use]
152 pub const fn with_strength(mut self, strength: BondStrength) -> Self {
153 self.strength = Some(strength);
154 self
155 }
156
157 #[must_use]
159 pub fn with_length(mut self, length: BondLength) -> Self {
160 self.length = Some(length);
161 self
162 }
163
164 pub fn try_with_length(self, value: f64, unit: &str) -> Result<Self, BondValidationError> {
171 Ok(self.with_length(BondLength::new(value, unit)?))
172 }
173
174 #[must_use]
176 pub fn with_angle_label(mut self, angle_label: BondDescriptor) -> Self {
177 self.angle_label = Some(angle_label);
178 self
179 }
180
181 pub fn try_with_angle_label(self, angle_label: &str) -> Result<Self, BondValidationError> {
187 let trimmed = angle_label.trim();
188 if trimmed.is_empty() {
189 return Err(BondValidationError::EmptyAngleLabel);
190 }
191
192 Ok(self.with_angle_label(BondDescriptor::new(trimmed)?))
193 }
194
195 #[must_use]
197 pub fn with_descriptor(mut self, descriptor: BondDescriptor) -> Self {
198 if !self.descriptors.contains(&descriptor) {
199 self.descriptors.push(descriptor);
200 }
201 self
202 }
203
204 pub fn try_with_descriptor(self, descriptor: &str) -> Result<Self, BondValidationError> {
210 Ok(self.with_descriptor(BondDescriptor::new(descriptor)?))
211 }
212}
213
214impl fmt::Display for Bond {
215 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
216 if let Some((first, rest)) = self.endpoints.split_first() {
217 write!(formatter, "{first}")?;
218 for endpoint in rest {
219 write!(formatter, "-{endpoint}")?;
220 }
221 write!(formatter, " {} bond", self.kind)?;
222 } else {
223 write!(formatter, "{} bond", self.kind)?;
224 }
225
226 if let Some(order) = self.order {
227 write!(formatter, " ({order})")?;
228 }
229
230 Ok(())
231 }
232}