Skip to main content

minion_sys/
error.rs

1//! Error types.
2
3use thiserror::Error;
4
5use crate::ffi;
6
7/// Wraps all error types returned by `minion-sys`.
8#[derive(Debug, Error)]
9#[non_exhaustive]
10pub enum MinionError {
11    /// An error has occurred during the execution of Minion.
12    #[error("runtime error: `{0}.to_string()`")]
13    RuntimeError(#[from] RuntimeError),
14
15    /// The input model uses Minion features that are not yet implemented in `minion_rs`.
16    #[error("not implemented: {0}")]
17    NotImplemented(String),
18
19    /// Catch-all error.
20    #[error(transparent)]
21    Other(#[from] anyhow::Error), // source and Display delegate to anyhow::Error
22}
23
24/// Errors thrown by Minion during execution.
25///
26/// These represent internal Minion C++ exceptions translated into Rust.
27///
28/// Invalid usage of this library should throw an error before Minion is even run. Therefore, these
29/// should be quite rare. Consider creating an issue on
30/// [Github](https://github.com/conjure-cp/conjure-oxide) if these occur regularly!
31#[derive(Debug, Error, Eq, PartialEq)]
32#[non_exhaustive]
33pub enum RuntimeError {
34    // These closely follow the MinionResult enum in Minion's libwrapper.h.
35    /// The model given to Minion is invalid.
36    #[error("the given instance is invalid: {0}")]
37    InvalidInstance(String),
38
39    /// The solver exceeded its time limit.
40    #[error("solver timed out")]
41    Timeout,
42
43    /// The solver ran out of memory.
44    #[error("solver ran out of memory")]
45    MemoryError,
46
47    /// A parse error occurred (e.g. bad variable name, duplicate name).
48    #[error("parse error: {0}")]
49    ParseError(String),
50
51    /// An invalid argument was provided.
52    #[error("invalid argument: {0}")]
53    InvalidArgument(String),
54
55    /// An unknown error has occurred.
56    #[error("an unknown error has occurred while running minion: {0}")]
57    UnknownError(String),
58}
59
60/// Check a MinionResult code and convert to Result.
61///
62/// On error, reads the thread-local error message from minion_error_message().
63pub fn check_minion_result(code: u32) -> Result<(), RuntimeError> {
64    #[allow(non_upper_case_globals)]
65    match code {
66        ffi::MinionResult_MINION_OK => Ok(()),
67        _ => {
68            let msg = unsafe {
69                let p = ffi::minion_error_message();
70                if p.is_null() {
71                    String::new()
72                } else {
73                    std::ffi::CStr::from_ptr(p).to_string_lossy().into_owned()
74                }
75            };
76            Err(match code {
77                ffi::MinionResult_MINION_INVALID_INSTANCE => RuntimeError::InvalidInstance(msg),
78                ffi::MinionResult_MINION_TIMEOUT => RuntimeError::Timeout,
79                ffi::MinionResult_MINION_MEMORY_ERROR => RuntimeError::MemoryError,
80                ffi::MinionResult_MINION_PARSE_ERROR => RuntimeError::ParseError(msg),
81                ffi::MinionResult_MINION_INVALID_ARGUMENT => RuntimeError::InvalidArgument(msg),
82                _ => RuntimeError::UnknownError(msg),
83            })
84        }
85    }
86}