Skip to main content

oximo_solver/
options.rs

1use std::time::Duration;
2
3/// Options valid for every solver, including NLP backends.
4/// Each backend's options struct embeds this via [`HasUniversal`]; the
5/// [`UniversalOptionsExt`] blanket impl then provides typed builder setters.
6#[derive(Clone, Debug, Default, PartialEq)]
7pub struct UniversalOptions {
8    pub time_limit: Option<Duration>,
9    pub threads: Option<u32>,
10    pub verbose: Option<bool>,
11}
12
13/// Implemented by every backend-specific options struct.
14/// Gives the [`UniversalOptionsExt`] blanket impl access to the embedded
15/// [`UniversalOptions`].
16pub trait HasUniversal {
17    fn universal(&self) -> &UniversalOptions;
18    fn universal_mut(&mut self) -> &mut UniversalOptions;
19}
20
21/// Builder setters available on every backend options struct.
22pub trait UniversalOptionsExt: HasUniversal + Sized {
23    #[must_use]
24    fn time_limit(mut self, d: Duration) -> Self {
25        self.universal_mut().time_limit = Some(d);
26        self
27    }
28
29    #[must_use]
30    fn threads(mut self, n: u32) -> Self {
31        self.universal_mut().threads = Some(n);
32        self
33    }
34
35    #[must_use]
36    fn verbose(mut self, on: bool) -> Self {
37        self.universal_mut().verbose = Some(on);
38        self
39    }
40}
41
42impl<T: HasUniversal> UniversalOptionsExt for T {}
43
44#[cfg(test)]
45mod tests {
46    use std::time::Duration;
47
48    use super::*;
49
50    #[derive(Default)]
51    struct TestOpts {
52        pub universal: UniversalOptions,
53    }
54    impl HasUniversal for TestOpts {
55        fn universal(&self) -> &UniversalOptions {
56            &self.universal
57        }
58        fn universal_mut(&mut self) -> &mut UniversalOptions {
59            &mut self.universal
60        }
61    }
62
63    #[test]
64    fn universal_default_is_all_none() {
65        let u = UniversalOptions::default();
66        assert!(u.time_limit.is_none());
67        assert!(u.threads.is_none());
68        assert!(u.verbose.is_none());
69    }
70
71    #[test]
72    fn universal_builder_chain() {
73        let o = TestOpts::default().time_limit(Duration::from_secs(60)).threads(4).verbose(false);
74        assert_eq!(o.universal.time_limit, Some(Duration::from_secs(60)));
75        assert_eq!(o.universal.threads, Some(4));
76        assert_eq!(o.universal.verbose, Some(false));
77    }
78
79    #[test]
80    fn universal_options_clone_eq() {
81        let a = TestOpts::default().threads(2).verbose(true);
82        let b = UniversalOptions { threads: Some(2), verbose: Some(true), ..Default::default() };
83        assert_eq!(a.universal, b);
84        let c = a.universal.clone();
85        assert_eq!(c, b);
86    }
87
88    #[test]
89    fn last_set_wins_for_same_field() {
90        let o = TestOpts::default().threads(1).threads(8);
91        assert_eq!(o.universal.threads, Some(8));
92    }
93}