1
//! Error types.
2

            
3
use thiserror::Error;
4

            
5
use crate::ffi;
6

            
7
/// Wraps all error types returned by `minion-sys`.
8
#[derive(Debug, Error)]
9
#[non_exhaustive]
10
pub 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]
33
pub 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().
63
962868
pub fn check_minion_result(code: u32) -> Result<(), RuntimeError> {
64
    #[allow(non_upper_case_globals)]
65
962868
    match code {
66
962868
        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
962868
}