Skip to main content

opensmiles/error/
parser.rs

1//! SMILES parsing errors.
2
3use thiserror::Error;
4
5use super::{BondError, MoleculeError, NodeError};
6
7/// Errors that can occur when parsing a SMILES string.
8#[derive(Debug, Clone, PartialEq, Error)]
9pub enum ParserError {
10    /// Feature not yet implemented.
11    #[error("feature not yet implemented")]
12    NotYetImplemented,
13
14    /// The molecule contains too many nodes (maximum 65535).
15    #[error("too many nodes in molecule (maximum 65535)")]
16    TooManyNodes,
17
18    #[error("At least one node is necessary before creating a bond")]
19    NoAtomToBond,
20
21    /// Unexpected character in SMILES string.
22    #[error("unexpected character '{0}' at position {1}")]
23    UnexpectedCharacter(char, usize),
24
25    /// Unexpected end of input.
26    #[error("unexpected end of input, expected: {0}")]
27    UnexpectedEndOfInput(String),
28
29    /// Bracket Atom must have an element
30    #[error("brackets atom must have an element")]
31    MissingElementInBracketAtom,
32
33    #[error("charge in bracket atom must have a sign")]
34    ChargeWithoutSign,
35
36    /// Missing closing parenthesis.
37    #[error("missing closing parenthesis ')'")]
38    UnclosedParenthesis,
39
40    /// Missing opening parenthesis.
41    #[error("missing opening parenthesis '('")]
42    UnopenedParenthesis,
43
44    /// Empty branch.
45    #[error("empty branch detected")]
46    EmptyBranch,
47
48    /// Unclosed ring.
49    #[error("unclosed ring(s): {0:?}")]
50    UnclosedRing(Vec<u8>),
51
52    /// Mismatched bond types for ring closure.
53    #[error("mismatched bond types for ring {0}")]
54    MismatchedRingBond(u8),
55
56    /// Bond without preceding atom.
57    #[error("bond without preceding atom")]
58    BondWithoutPrecedingAtom,
59
60    /// Bond without following atom.
61    #[error("bond without following atom")]
62    BondWithoutFollowingAtom,
63
64    /// Hydrogens having hydrogens count is illegal
65    #[error("hydrogens cannot have hydrogens count")]
66    HydrogenWithHydrogenCount,
67
68    #[error("charge should be between -15 and +15 {0}")]
69    ChargeOutOfRange(String),
70
71    #[error("hydrogen cannot be greater than 9 {0}")]
72    HydrogenOutOfRange(String),
73
74    #[error("invalid chirality class: {0} at position {1}")]
75    InvalidChiralityClass(String, usize),
76
77    #[error("invalid chirality specification: {0} at position {1}")]
78    InvalidChiralitySpec(String, usize),
79
80    /// Atom bonded to itself (e.g., C11).
81    #[error("atom cannot be bonded to itself (ring {0})")]
82    SelfBond(u8),
83
84    /// Duplicate bond between the same pair of atoms (e.g., C12CCCCC12).
85    #[error("duplicate bond between atoms {0} and {1}")]
86    DuplicateBond(u16, u16),
87
88    /// Error from molecule construction.
89    #[error(transparent)]
90    MoleculeError(#[from] MoleculeError),
91
92    /// Error from a node.
93    #[error(transparent)]
94    NodeError(#[from] NodeError),
95
96    /// Error from a bond.
97    #[error(transparent)]
98    BondError(#[from] BondError),
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104
105    #[test]
106    fn error_messages_are_descriptive() {
107        assert_eq!(
108            ParserError::NotYetImplemented.to_string(),
109            "feature not yet implemented"
110        );
111
112        assert_eq!(
113            ParserError::TooManyNodes.to_string(),
114            "too many nodes in molecule (maximum 65535)"
115        );
116
117        assert_eq!(
118            ParserError::UnexpectedCharacter('$', 5).to_string(),
119            "unexpected character '$' at position 5"
120        );
121
122        assert_eq!(
123            ParserError::UnexpectedEndOfInput("element".to_string()).to_string(),
124            "unexpected end of input, expected: element"
125        );
126
127        assert_eq!(
128            ParserError::UnclosedParenthesis.to_string(),
129            "missing closing parenthesis ')'"
130        );
131
132        assert_eq!(
133            ParserError::UnclosedRing(vec!(1, 2, 5)).to_string(),
134            "unclosed ring(s): [1, 2, 5]"
135        );
136
137        assert_eq!(
138            ParserError::MismatchedRingBond(2).to_string(),
139            "mismatched bond types for ring 2"
140        );
141
142        assert_eq!(
143            ParserError::BondWithoutPrecedingAtom.to_string(),
144            "bond without preceding atom"
145        );
146
147        assert_eq!(
148            ParserError::BondWithoutFollowingAtom.to_string(),
149            "bond without following atom"
150        );
151    }
152
153    #[test]
154    fn molecule_error_conversion() {
155        let mol_err = MoleculeError::NodeError(super::super::NodeError::UndefinedHydrogen);
156        let parser_err: ParserError = mol_err.into();
157
158        assert!(matches!(parser_err, ParserError::MoleculeError(_)));
159    }
160
161    #[test]
162    fn node_error_conversion() {
163        let node_err = super::super::NodeError::InvalidHydrogen(99);
164        let parser_err: ParserError = node_err.into();
165
166        assert!(matches!(parser_err, ParserError::NodeError(_)));
167        assert_eq!(parser_err.to_string(), "invalid hydrogen count: 99");
168    }
169
170    #[test]
171    fn bond_error_conversion() {
172        let bond_err = super::super::BondError::UnknownBond('c');
173        let parser_err: ParserError = bond_err.into();
174
175        assert!(matches!(parser_err, ParserError::BondError(_)));
176        assert_eq!(parser_err.to_string(), "unknown bond: 'c'");
177    }
178}