cplex_rs/
errors.rs

1use std::{
2    ffi::{c_int, CString},
3    ops::Not,
4};
5
6pub type Result<T> = std::result::Result<T, Error>;
7
8use ffi::{cpxenv, cpxlp, CPXgeterrorstring, CPXgetijdiv, CPXsolninfo, CPXMESSAGEBUFSIZE};
9use log::error;
10use thiserror::Error;
11
12#[derive(Error, Debug)]
13pub enum Error {
14    #[error("Cplex error: {0}")]
15    Cplex(#[from] Cplex),
16    #[error("Input error: {0}")]
17    Input(#[from] Input),
18}
19
20#[derive(Error, Debug)]
21#[error("Cplex error status {code}: {message}")]
22pub enum Cplex {
23    Unbounded { code: c_int, message: String },
24    Unfeasible { code: c_int, message: String },
25    Other { code: c_int, message: String },
26}
27
28impl Cplex {
29    pub(crate) fn from_code(env: *const cpxenv, lp: *const cpxlp, code: c_int) -> Cplex {
30        let mut buf = vec![0u8; CPXMESSAGEBUFSIZE as usize];
31        let ptr = unsafe { CPXgeterrorstring(env, code, buf.as_mut_ptr() as *mut i8) };
32        let message = ptr
33            .is_null()
34            .not()
35            .then_some(())
36            .and_then(|_| CString::from_vec_with_nul(buf).ok())
37            .and_then(|cs| cs.into_string().ok())
38            .unwrap_or_else(|| "Unable to extract error message".to_string());
39
40        if lp.is_null() {
41            return Self::Other { code, message };
42        }
43
44        if !Self::is_feasible(env, lp) {
45            Self::Unfeasible { code, message }
46        } else if !Self::is_bounded(env, lp) {
47            Self::Unbounded { code, message }
48        } else {
49            Self::Other { code, message }
50        }
51    }
52
53    fn is_bounded(env: *const cpxenv, lp: *const cpxlp) -> bool {
54        let mut i = 0;
55        let mut j = 0;
56        match unsafe { CPXgetijdiv(env, lp, &mut i, &mut j) } {
57            0 => j == -1,
58            _ => {
59                error!("Unable to determine if problem is bounded, assuming it is");
60                true
61            }
62        }
63    }
64
65    fn is_feasible(env: *const cpxenv, lp: *const cpxlp) -> bool {
66        let mut lpstat = 0;
67        let mut stype = 0;
68        let mut pfeas = 0;
69        let mut dfeas = 0;
70        match unsafe { CPXsolninfo(env, lp, &mut lpstat, &mut stype, &mut pfeas, &mut dfeas) } {
71            0 => pfeas != 0,
72            _ => {
73                error!("Unable to determine if problem is feasible, assuming it is");
74                true
75            }
76        }
77    }
78
79    pub(crate) fn env_error(code: c_int) -> Cplex {
80        let message = "Error encountered when constructing CPLEX env".to_owned();
81        Self::Other { code, message }
82    }
83}
84
85#[derive(Error, Debug)]
86#[error("Input error: {message}")]
87pub struct Input {
88    pub message: String,
89}
90
91impl Input {
92    pub(crate) fn from_message(message: String) -> Input {
93        Self { message }
94    }
95}