solverforge_solver/termination/
composite.rs

1//! Composite termination conditions (AND/OR).
2
3use std::fmt::Debug;
4
5use solverforge_core::domain::PlanningSolution;
6
7use super::Termination;
8use crate::scope::SolverScope;
9
10/// Combines multiple terminations with OR logic.
11///
12/// Terminates when ANY of the child terminations triggers.
13pub struct OrCompositeTermination<S: PlanningSolution> {
14    terminations: Vec<Box<dyn Termination<S>>>,
15}
16
17impl<S: PlanningSolution> Debug for OrCompositeTermination<S> {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        f.debug_struct("OrCompositeTermination")
20            .field("count", &self.terminations.len())
21            .finish()
22    }
23}
24
25impl<S: PlanningSolution> OrCompositeTermination<S> {
26    pub fn new(terminations: Vec<Box<dyn Termination<S>>>) -> Self {
27        Self { terminations }
28    }
29}
30
31impl<S: PlanningSolution> Termination<S> for OrCompositeTermination<S> {
32    fn is_terminated(&self, solver_scope: &SolverScope<S>) -> bool {
33        self.terminations
34            .iter()
35            .any(|t| t.is_terminated(solver_scope))
36    }
37}
38
39/// Combines multiple terminations with AND logic.
40///
41/// All terminations must agree before solving terminates.
42pub struct AndCompositeTermination<S: PlanningSolution> {
43    terminations: Vec<Box<dyn Termination<S>>>,
44}
45
46impl<S: PlanningSolution> Debug for AndCompositeTermination<S> {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        f.debug_struct("AndCompositeTermination")
49            .field("count", &self.terminations.len())
50            .finish()
51    }
52}
53
54impl<S: PlanningSolution> AndCompositeTermination<S> {
55    pub fn new(terminations: Vec<Box<dyn Termination<S>>>) -> Self {
56        Self { terminations }
57    }
58}
59
60impl<S: PlanningSolution> Termination<S> for AndCompositeTermination<S> {
61    fn is_terminated(&self, solver_scope: &SolverScope<S>) -> bool {
62        !self.terminations.is_empty()
63            && self
64                .terminations
65                .iter()
66                .all(|t| t.is_terminated(solver_scope))
67    }
68}