use super::outer_loop_fsm::{OuterLoopIterationExit, OuterLoopIterationPhase, ReflectBranchCtl};
use super::outer_loop_iteration_reduce::{
OuterLoopReflectReduceAction, reduce_outer_loop_post_tools_exit,
reduce_outer_loop_reflect_branch,
};
#[derive(Debug, Clone)]
pub struct OuterLoopDriver {
phase: OuterLoopIterationPhase,
last_reflect: Option<ReflectBranchCtl>,
last_exit: Option<OuterLoopIterationExit>,
}
impl OuterLoopDriver {
pub fn new() -> Self {
Self {
phase: OuterLoopIterationPhase::IterationEnter,
last_reflect: None,
last_exit: None,
}
}
#[inline]
pub fn current_phase(&self) -> OuterLoopIterationPhase {
self.phase
}
pub fn record_phase(&mut self, phase: OuterLoopIterationPhase) {
debug_assert!(
self.phase_transition_allowed(phase),
"illegal outer_loop_step {:?} → {:?}",
self.phase,
phase
);
self.phase = phase;
}
pub fn phase_str(&self) -> &'static str {
self.phase.as_str()
}
pub fn record_reflect_branch(&mut self, ctl: ReflectBranchCtl) -> OuterLoopReflectReduceAction {
self.last_reflect = Some(ctl);
reduce_outer_loop_reflect_branch(ctl)
}
pub fn last_reflect_branch(&self) -> Option<ReflectBranchCtl> {
self.last_reflect
}
pub fn record_iteration_exit(&mut self, exit: OuterLoopIterationExit) {
self.last_exit = Some(exit);
}
pub fn last_iteration_exit(&self) -> Option<OuterLoopIterationExit> {
self.last_exit
}
pub fn decide_post_tools_exit(&self, task_level_early_stop: bool) -> OuterLoopIterationExit {
reduce_outer_loop_post_tools_exit(task_level_early_stop)
}
fn phase_transition_allowed(&self, next: OuterLoopIterationPhase) -> bool {
use OuterLoopIterationPhase::*;
if self.phase == next {
return true;
}
matches!(
(self.phase, next),
(IterationEnter, PrepareContextDone)
| (PrepareContextDone, AfterPlannerModel)
| (AfterPlannerModel, ReflectDecided)
| (ReflectDecided, ToolsExecute)
| (_, IterationEnter)
)
}
}
impl Default for OuterLoopDriver {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn happy_path_phase_sequence() {
let mut d = OuterLoopDriver::new();
assert_eq!(d.current_phase(), OuterLoopIterationPhase::IterationEnter);
d.record_phase(OuterLoopIterationPhase::PrepareContextDone);
d.record_phase(OuterLoopIterationPhase::AfterPlannerModel);
let action = d.record_reflect_branch(ReflectBranchCtl::ProceedToTools);
assert_eq!(action, OuterLoopReflectReduceAction::ProceedToTools);
d.record_phase(OuterLoopIterationPhase::ReflectDecided);
d.record_phase(OuterLoopIterationPhase::ToolsExecute);
let exit = d.decide_post_tools_exit(false);
d.record_iteration_exit(exit);
assert_eq!(
d.last_iteration_exit(),
Some(OuterLoopIterationExit::ContinueNextIteration)
);
d.record_phase(OuterLoopIterationPhase::IterationEnter);
}
#[test]
fn reflect_break_stops_outer() {
let mut d = OuterLoopDriver::new();
d.record_phase(OuterLoopIterationPhase::PrepareContextDone);
d.record_phase(OuterLoopIterationPhase::AfterPlannerModel);
let action = d.record_reflect_branch(ReflectBranchCtl::BreakOuter);
assert_eq!(action, OuterLoopReflectReduceAction::StopOuterLoop);
d.record_phase(OuterLoopIterationPhase::ReflectDecided);
d.record_iteration_exit(OuterLoopIterationExit::StopOuterLoop);
assert_eq!(
d.last_iteration_exit(),
Some(OuterLoopIterationExit::StopOuterLoop)
);
}
}