1use crate::types::Index;
14use std::fmt;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[allow(non_camel_case_types)]
20pub enum ExceptionKind {
21 OPTION_INVALID,
23 OPTION_ALREADY_REGISTERED,
24 DYNAMIC_LIBRARY_FAILURE,
25 UNIMPLEMENTED_LINALG_METHOD_CALLED,
27 UNKNOWN_MATRIX_TYPE,
28 UNKNOWN_VECTOR_TYPE,
29 LAPACK_NOT_INCLUDED,
30 METADATA_ERROR,
31 TINY_STEP_DETECTED,
33 ACCEPTABLE_POINT_REACHED,
34 STEP_COMPUTATION_FAILED,
35 LOCALLY_INFEASIBLE,
36 FEASIBILITY_PROBLEM_SOLVED,
37 RESTORATION_FAILED,
39 RESTORATION_CONVERGED_TO_FEASIBLE_POINT,
40 RESTORATION_MAXITER_EXCEEDED,
41 RESTORATION_CPUTIME_EXCEEDED,
42 RESTORATION_WALLTIME_EXCEEDED,
43 RESTORATION_USER_STOP,
44 FATAL_ERROR_IN_LINEAR_SOLVER,
46 ERROR_IN_LINEAR_SCALING_METHOD,
47 NONPOSITIVE_SCALING_FACTOR,
48 USER_SCALING_NOT_IMPLEMENTED,
49 INVALID_NLP,
51 INVALID_TNLP,
52 INVALID_STDINTERFACE_NLP,
53 INVALID_WARMSTART,
54 INCONSISTENT_BOUNDS,
55 TOO_FEW_DOF,
56 ERROR_IN_TNLP_DERIVATIVE_TEST,
57 IPOPT_APPLICATION_ERROR,
59 FAILED_INITIALIZATION,
60 INTERNAL_ABORT,
61 ERROR_CONVERTING_STRING_TO_ENUM,
63}
64
65impl ExceptionKind {
66 pub fn name(self) -> &'static str {
68 use ExceptionKind::*;
69 match self {
70 OPTION_INVALID => "OPTION_INVALID",
71 OPTION_ALREADY_REGISTERED => "OPTION_ALREADY_REGISTERED",
72 DYNAMIC_LIBRARY_FAILURE => "DYNAMIC_LIBRARY_FAILURE",
73 UNIMPLEMENTED_LINALG_METHOD_CALLED => "UNIMPLEMENTED_LINALG_METHOD_CALLED",
74 UNKNOWN_MATRIX_TYPE => "UNKNOWN_MATRIX_TYPE",
75 UNKNOWN_VECTOR_TYPE => "UNKNOWN_VECTOR_TYPE",
76 LAPACK_NOT_INCLUDED => "LAPACK_NOT_INCLUDED",
77 METADATA_ERROR => "METADATA_ERROR",
78 TINY_STEP_DETECTED => "TINY_STEP_DETECTED",
79 ACCEPTABLE_POINT_REACHED => "ACCEPTABLE_POINT_REACHED",
80 STEP_COMPUTATION_FAILED => "STEP_COMPUTATION_FAILED",
81 LOCALLY_INFEASIBLE => "LOCALLY_INFEASIBLE",
82 FEASIBILITY_PROBLEM_SOLVED => "FEASIBILITY_PROBLEM_SOLVED",
83 RESTORATION_FAILED => "RESTORATION_FAILED",
84 RESTORATION_CONVERGED_TO_FEASIBLE_POINT => "RESTORATION_CONVERGED_TO_FEASIBLE_POINT",
85 RESTORATION_MAXITER_EXCEEDED => "RESTORATION_MAXITER_EXCEEDED",
86 RESTORATION_CPUTIME_EXCEEDED => "RESTORATION_CPUTIME_EXCEEDED",
87 RESTORATION_WALLTIME_EXCEEDED => "RESTORATION_WALLTIME_EXCEEDED",
88 RESTORATION_USER_STOP => "RESTORATION_USER_STOP",
89 FATAL_ERROR_IN_LINEAR_SOLVER => "FATAL_ERROR_IN_LINEAR_SOLVER",
90 ERROR_IN_LINEAR_SCALING_METHOD => "ERROR_IN_LINEAR_SCALING_METHOD",
91 NONPOSITIVE_SCALING_FACTOR => "NONPOSITIVE_SCALING_FACTOR",
92 USER_SCALING_NOT_IMPLEMENTED => "USER_SCALING_NOT_IMPLEMENTED",
93 INVALID_NLP => "INVALID_NLP",
94 INVALID_TNLP => "INVALID_TNLP",
95 INVALID_STDINTERFACE_NLP => "INVALID_STDINTERFACE_NLP",
96 INVALID_WARMSTART => "INVALID_WARMSTART",
97 INCONSISTENT_BOUNDS => "INCONSISTENT_BOUNDS",
98 TOO_FEW_DOF => "TOO_FEW_DOF",
99 ERROR_IN_TNLP_DERIVATIVE_TEST => "ERROR_IN_TNLP_DERIVATIVE_TEST",
100 IPOPT_APPLICATION_ERROR => "IPOPT_APPLICATION_ERROR",
101 FAILED_INITIALIZATION => "FAILED_INITIALIZATION",
102 INTERNAL_ABORT => "INTERNAL_ABORT",
103 ERROR_CONVERTING_STRING_TO_ENUM => "ERROR_CONVERTING_STRING_TO_ENUM",
104 }
105 }
106}
107
108impl fmt::Display for ExceptionKind {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 f.write_str(self.name())
111 }
112}
113
114#[derive(Debug, Clone)]
119pub struct SolverException {
120 pub kind: ExceptionKind,
121 pub message: String,
122 pub file: &'static str,
123 pub line: Index,
124}
125
126impl SolverException {
127 pub fn new(
128 kind: ExceptionKind,
129 message: impl Into<String>,
130 file: &'static str,
131 line: Index,
132 ) -> Self {
133 Self {
134 kind,
135 message: message.into(),
136 file,
137 line,
138 }
139 }
140}
141
142impl fmt::Display for SolverException {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 write!(
146 f,
147 "Exception of type: {} in file \"{}\" at line {}:\n Exception message: {}",
148 self.kind, self.file, self.line, self.message
149 )
150 }
151}
152
153impl std::error::Error for SolverException {}
154
155#[track_caller]
168pub fn option_invalid(name: &str, message: impl fmt::Display) -> SolverException {
169 let loc = std::panic::Location::caller();
170 SolverException::new(
171 ExceptionKind::OPTION_INVALID,
172 format!("Option \"{name}\": {message}."),
173 loc.file(),
174 loc.line() as Index,
175 )
176}
177
178#[macro_export]
181macro_rules! throw {
182 ($kind:expr_2021, $msg:expr_2021) => {
183 return ::core::result::Result::Err($crate::exception::SolverException::new(
184 $kind,
185 $msg,
186 file!(),
187 line!() as $crate::types::Index,
188 ))
189 };
190}
191
192#[macro_export]
194macro_rules! assert_exc {
195 ($cond:expr_2021, $kind:expr_2021, $msg:expr_2021) => {
196 if !($cond) {
197 let mut newmsg = ::std::string::String::from(stringify!($cond));
198 newmsg.push_str(" evaluated false: ");
199 newmsg.push_str($msg);
200 return ::core::result::Result::Err($crate::exception::SolverException::new(
201 $kind,
202 newmsg,
203 file!(),
204 line!() as $crate::types::Index,
205 ));
206 }
207 };
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn display_matches_upstream_format() {
216 let e = SolverException::new(
217 ExceptionKind::OPTION_INVALID,
218 "bad option",
219 "src/foo.rs",
220 42,
221 );
222 let s = format!("{}", e);
223 assert!(s.contains("Exception of type: OPTION_INVALID"));
224 assert!(s.contains("src/foo.rs"));
225 assert!(s.contains("line 42"));
226 assert!(s.contains("Exception message: bad option"));
227 }
228
229 #[test]
230 fn names_all_round_trip() {
231 for k in [
232 ExceptionKind::OPTION_INVALID,
233 ExceptionKind::TINY_STEP_DETECTED,
234 ExceptionKind::RESTORATION_FAILED,
235 ExceptionKind::LOCALLY_INFEASIBLE,
236 ] {
237 assert!(!k.name().is_empty());
238 assert_eq!(format!("{k}"), k.name());
239 }
240 }
241}