dreid_forge/forge/error.rs
1//! Error types for DREIDING parameterization.
2//!
3//! This module defines the error type used throughout the forge module.
4//! Errors are categorized by source: parameter parsing, atom typing,
5//! charge calculation, and missing force field parameters.
6
7use thiserror::Error;
8
9/// Errors that can occur during DREIDING parameterization.
10///
11/// This enum covers all failure modes when running the [`forge`](super::forge)
12/// function, including configuration parsing, atom typing, charge calculation,
13/// and parameter lookup failures.
14#[derive(Debug, Error)]
15pub enum Error {
16 /// Failed to parse force field parameters TOML.
17 #[error("failed to parse force field parameters: {0}")]
18 ParameterParse(#[from] toml::de::Error),
19
20 /// Failed to parse custom atom typing rules.
21 #[error("failed to parse custom typing rules: {0}")]
22 RuleParse(String),
23
24 /// Atom typing assignment failed.
25 ///
26 /// Occurs when the molecular graph cannot be typed due to unsupported
27 /// elements, unusual bonding patterns, or rule matching failures.
28 #[error("atom typing failed: {0}")]
29 AtomTyping(String),
30
31 /// Charge calculation failed.
32 ///
33 /// Occurs when the QEq solver fails to converge or encounters
34 /// invalid input data.
35 #[error("charge calculation failed: {0}")]
36 ChargeCalculation(String),
37
38 /// Hybrid charge method requires biological metadata.
39 ///
40 /// Occurs when [`ChargeMethod::Hybrid`](crate::ChargeMethod::Hybrid) is selected
41 /// but the input system has no [`BioMetadata`](crate::BioMetadata).
42 #[error("hybrid charge method requires biological metadata (bio_metadata is None)")]
43 MissingBioMetadata,
44
45 /// Hybrid charge assignment failed for a specific residue.
46 #[error(
47 "hybrid charge assignment failed for residue {residue_name} at chain {chain_id} residue {residue_id}: {detail}"
48 )]
49 HybridChargeAssignment {
50 /// Chain identifier.
51 chain_id: String,
52 /// Residue sequence number.
53 residue_id: i32,
54 /// Residue name.
55 residue_name: String,
56 /// Description of the problem.
57 detail: String,
58 },
59
60 /// Required force field parameter not found.
61 ///
62 /// Occurs when an atom type is assigned but no corresponding
63 /// parameters exist in the force field parameter file.
64 #[error("missing force field parameter for atom type '{atom_type}': {detail}")]
65 MissingParameter {
66 /// The atom type that is missing parameters.
67 atom_type: String,
68 /// Description of which parameter is missing.
69 detail: String,
70 },
71
72 /// Invalid bond definition in the input system.
73 #[error("invalid bond between atoms {i} and {j}: {detail}")]
74 InvalidBond {
75 /// First atom index.
76 i: usize,
77 /// Second atom index.
78 j: usize,
79 /// Description of the problem.
80 detail: String,
81 },
82
83 /// The input system contains no atoms.
84 #[error("input system is empty: at least one atom is required")]
85 EmptySystem,
86
87 /// Internal data conversion error.
88 #[error("internal conversion error: {0}")]
89 Conversion(String),
90}
91
92impl From<dreid_typer::TyperError> for Error {
93 fn from(e: dreid_typer::TyperError) -> Self {
94 Error::AtomTyping(e.to_string())
95 }
96}
97
98impl From<cheq::CheqError> for Error {
99 fn from(e: cheq::CheqError) -> Self {
100 Error::ChargeCalculation(e.to_string())
101 }
102}
103
104impl Error {
105 /// Creates a [`HybridChargeAssignment`](Error::HybridChargeAssignment) error.
106 ///
107 /// # Arguments
108 ///
109 /// * `chain_id` — Chain identifier
110 /// * `residue_id` — Residue sequence number
111 /// * `residue_name` — Residue name
112 /// * `details` — Description of the problem
113 ///
114 /// # Returns
115 ///
116 /// A [`HybridChargeAssignment`](Error::HybridChargeAssignment) error variant.
117 pub fn hybrid_charge_assignment(
118 chain_id: impl Into<String>,
119 residue_id: i32,
120 residue_name: &str,
121 details: impl Into<String>,
122 ) -> Self {
123 Self::HybridChargeAssignment {
124 chain_id: chain_id.into(),
125 residue_id,
126 residue_name: residue_name.to_string(),
127 detail: details.into(),
128 }
129 }
130
131 /// Creates a [`MissingParameter`](Error::MissingParameter) error.
132 ///
133 /// # Arguments
134 ///
135 /// * `atom_type` — The atom type that is missing parameters
136 /// * `details` — Description of which parameter is missing
137 ///
138 /// # Returns
139 ///
140 /// A [`MissingParameter`](Error::MissingParameter) error variant.
141 pub fn missing_parameter(atom_type: &str, details: impl Into<String>) -> Self {
142 Self::MissingParameter {
143 atom_type: atom_type.to_string(),
144 detail: details.into(),
145 }
146 }
147
148 /// Creates an [`InvalidBond`](Error::InvalidBond) error.
149 ///
150 /// # Arguments
151 ///
152 /// * `i` — First atom index
153 /// * `j` — Second atom index
154 /// * `details` — Description of the bond problem
155 ///
156 /// # Returns
157 ///
158 /// An [`InvalidBond`](Error::InvalidBond) error variant.
159 pub fn invalid_bond(i: usize, j: usize, details: impl Into<String>) -> Self {
160 Self::InvalidBond {
161 i,
162 j,
163 detail: details.into(),
164 }
165 }
166}