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#[macro_export]
158macro_rules! throw {
159 ($kind:expr, $msg:expr) => {
160 return ::core::result::Result::Err($crate::exception::SolverException::new(
161 $kind,
162 $msg,
163 file!(),
164 line!() as $crate::types::Index,
165 ))
166 };
167}
168
169#[macro_export]
171macro_rules! assert_exc {
172 ($cond:expr, $kind:expr, $msg:expr) => {
173 if !($cond) {
174 let mut newmsg = ::std::string::String::from(stringify!($cond));
175 newmsg.push_str(" evaluated false: ");
176 newmsg.push_str($msg);
177 return ::core::result::Result::Err($crate::exception::SolverException::new(
178 $kind,
179 newmsg,
180 file!(),
181 line!() as $crate::types::Index,
182 ));
183 }
184 };
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn display_matches_upstream_format() {
193 let e = SolverException::new(
194 ExceptionKind::OPTION_INVALID,
195 "bad option",
196 "src/foo.rs",
197 42,
198 );
199 let s = format!("{}", e);
200 assert!(s.contains("Exception of type: OPTION_INVALID"));
201 assert!(s.contains("src/foo.rs"));
202 assert!(s.contains("line 42"));
203 assert!(s.contains("Exception message: bad option"));
204 }
205
206 #[test]
207 fn names_all_round_trip() {
208 for k in [
209 ExceptionKind::OPTION_INVALID,
210 ExceptionKind::TINY_STEP_DETECTED,
211 ExceptionKind::RESTORATION_FAILED,
212 ExceptionKind::LOCALLY_INFEASIBLE,
213 ] {
214 assert!(!k.name().is_empty());
215 assert_eq!(format!("{k}"), k.name());
216 }
217 }
218}