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}