Skip to main content

use_molecule/
molecular_atom.rs

1use std::fmt;
2
3use crate::{AtomLabel, MoleculeValidationError};
4
5/// A validated atom identifier.
6#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
7pub struct MolecularAtomId(String);
8
9impl MolecularAtomId {
10    /// Creates an atom identifier.
11    ///
12    /// # Errors
13    ///
14    /// Returns [`MoleculeValidationError::EmptyAtomId`] when `id` is empty after trimming.
15    pub fn new(id: &str) -> Result<Self, MoleculeValidationError> {
16        let trimmed = id.trim();
17        if trimmed.is_empty() {
18            Err(MoleculeValidationError::EmptyAtomId)
19        } else {
20            Ok(Self(trimmed.to_owned()))
21        }
22    }
23
24    /// Returns the atom identifier text.
25    #[must_use]
26    pub fn as_str(&self) -> &str {
27        &self.0
28    }
29
30    /// Consumes the identifier and returns the owned text.
31    #[must_use]
32    pub fn into_string(self) -> String {
33        self.0
34    }
35}
36
37impl AsRef<str> for MolecularAtomId {
38    fn as_ref(&self) -> &str {
39        self.as_str()
40    }
41}
42
43impl TryFrom<&str> for MolecularAtomId {
44    type Error = MoleculeValidationError;
45
46    fn try_from(value: &str) -> Result<Self, Self::Error> {
47        Self::new(value)
48    }
49}
50
51impl fmt::Display for MolecularAtomId {
52    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
53        formatter.write_str(self.as_str())
54    }
55}
56
57/// An explicit atom entry in a molecule.
58#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct MolecularAtom {
60    label: AtomLabel,
61    id: Option<MolecularAtomId>,
62}
63
64impl MolecularAtom {
65    /// Creates a molecular atom from an atom label.
66    ///
67    /// # Errors
68    ///
69    /// Returns [`MoleculeValidationError`] when `label` is empty or does not match the supported
70    /// element-symbol shape.
71    pub fn new(label: &str) -> Result<Self, MoleculeValidationError> {
72        Ok(Self {
73            label: AtomLabel::new(label)?,
74            id: None,
75        })
76    }
77
78    /// Returns the atom label.
79    #[must_use]
80    pub const fn label(&self) -> &AtomLabel {
81        &self.label
82    }
83
84    /// Returns the optional atom identifier.
85    #[must_use]
86    pub const fn id(&self) -> Option<&MolecularAtomId> {
87        self.id.as_ref()
88    }
89
90    /// Sets the atom identifier from a validated value.
91    #[must_use]
92    pub fn with_id(mut self, id: MolecularAtomId) -> Self {
93        self.id = Some(id);
94        self
95    }
96
97    /// Sets the atom identifier after validation.
98    ///
99    /// # Errors
100    ///
101    /// Returns [`MoleculeValidationError::EmptyAtomId`] when `id` is empty after trimming.
102    pub fn try_with_id(self, id: &str) -> Result<Self, MoleculeValidationError> {
103        Ok(self.with_id(MolecularAtomId::new(id)?))
104    }
105}
106
107impl fmt::Display for MolecularAtom {
108    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
109        match &self.id {
110            Some(id) => write!(formatter, "{}#{id}", self.label),
111            None => write!(formatter, "{}", self.label),
112        }
113    }
114}