diff --git a/src/lib.rs b/src/lib.rs index 45f56de..67d3d67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,10 +119,9 @@ use highs_sys::*; pub use matrix_col::{ColMatrix, Row}; pub use matrix_row::{Col, RowMatrix}; +pub use options::{HighsOptionValue, TrySetOptionError}; pub use status::{HighsModelStatus, HighsSolutionStatus, HighsStatus}; -use crate::options::HighsOptionValue; - /// A problem where variables are declared first, and constraints are then added dynamically. /// See [`Problem`](Problem#impl-1). pub type RowProblem = Problem; @@ -388,7 +387,28 @@ impl Model { /// model.set_option("threads", 4); // solve on 4 threads /// ``` pub fn set_option>, V: HighsOptionValue>(&mut self, option: STR, value: V) { - self.highs.set_option(option, value) + self.try_set_option(option, value).unwrap() + } + + /// Try to set a custom parameter on the model, returning an error if it fails. + /// For the list of available options and their documentation, see: + /// + /// + /// It will fail if the option does not exist or the value is invalid. + /// + /// ``` + /// # use highs::ColProblem; + /// # use highs::Sense::Maximise; + /// let mut model = ColProblem::default().optimise(Maximise); + /// assert!(model.try_set_option("presolve", "off").is_ok()); // disable the presolver + /// assert!(model.try_set_option("made_up_option", true).is_err()); + /// ``` + pub fn try_set_option>, V: HighsOptionValue>( + &mut self, + option: STR, + value: V, + ) -> Result<(), TrySetOptionError> { + self.highs.try_set_option(option, value) } /// Set the number of threads to use when solving the model. @@ -703,16 +723,22 @@ impl HighsPtr { // setting log_file seems to cause a double free in Highs. // See https://github.com/rust-or/highs/issues/3 // self.set_option(&b"log_file"[..], ""); - self.set_option(&b"output_flag"[..], false); - self.set_option(&b"log_to_console"[..], false); + self.try_set_option(&b"output_flag"[..], false).unwrap(); + self.try_set_option(&b"log_to_console"[..], false).unwrap(); } /// Set a custom parameter on the model - pub fn set_option>, V: HighsOptionValue>(&mut self, option: STR, value: V) { - let c_str = CString::new(option).expect("invalid option name"); + pub fn try_set_option>, V: HighsOptionValue>( + &mut self, + option: STR, + value: V, + ) -> Result<(), TrySetOptionError> { + let c_str = CString::new(option).expect("Option name contains NUL char"); let status = unsafe { value.apply_to_highs(self.mut_ptr(), c_str.as_ptr()) }; - try_handle_status(status, "Highs_setOptionValue") - .expect("An error was encountered in HiGHS."); + match try_handle_status(status, "Highs_setOptionValue") { + Ok(_) => Ok(()), + Err(_) => Err(TrySetOptionError {}), + } } /// Number of variables @@ -1071,6 +1097,4 @@ mod test { assert_eq!(status, highs_sys::STATUS_OK); assert_eq!(value, 2); } - - } diff --git a/src/options.rs b/src/options.rs index d83f82d..c846012 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,7 +1,28 @@ +use std::error::Error; use std::ffi::{c_void, CStr, CString}; +use std::fmt::{Debug, Display}; use std::os::raw::{c_char, c_int}; +/// An error occurred while trying to set a model option +#[derive(Debug, Clone)] +pub struct TrySetOptionError; + +impl Display for TrySetOptionError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Error setting model option") + } +} + +impl Error for TrySetOptionError {} + +/// A trait defining the possible value types for HiGHS model options pub trait HighsOptionValue { + /// Apply the given value to the given option for the HiGHS model + /// + /// # Safety + /// + /// This function should only be called with valid pointers to `highs` and `option`. `option` + /// should be a NUL-terminated C-string. unsafe fn apply_to_highs(self, highs: *mut c_void, option: *const c_char) -> c_int; }