use std::collections::HashMap;
use crate::types::WorkerId;
use super::reason::TerminationVerdict;
#[derive(Debug, Clone, Default)]
pub struct CompletionState {
worker_results: HashMap<WorkerId, WorkerResult>,
exploration_done: bool,
exploration_exhausted: bool,
environment_done: bool,
final_verdict: Option<TerminationVerdict>,
completion_tick: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct WorkerResult {
pub success: bool,
pub message: Option<String>,
pub tick: u64,
}
impl CompletionState {
pub fn new() -> Self {
Self::default()
}
pub fn record_worker_done(
&mut self,
worker_id: WorkerId,
success: bool,
message: Option<String>,
tick: u64,
) {
self.worker_results.insert(
worker_id,
WorkerResult {
success,
message,
tick,
},
);
if success {
self.environment_done = true;
if self.completion_tick.is_none() {
self.completion_tick = Some(tick);
}
}
}
pub fn mark_exploration_done(&mut self, exhausted: bool) {
self.exploration_done = true;
self.exploration_exhausted = exhausted;
}
pub fn set_verdict(&mut self, verdict: TerminationVerdict) {
if self.final_verdict.is_none() {
self.final_verdict = Some(verdict);
}
}
pub fn verdict(&self) -> Option<&TerminationVerdict> {
self.final_verdict.as_ref()
}
pub fn is_environment_done(&self) -> bool {
self.environment_done
}
pub fn is_exploration_done(&self) -> bool {
self.exploration_done
}
pub fn is_exploration_exhausted(&self) -> bool {
self.exploration_exhausted
}
pub fn has_verdict(&self) -> bool {
self.final_verdict.is_some()
}
pub fn completed_workers(&self) -> &HashMap<WorkerId, WorkerResult> {
&self.worker_results
}
pub fn any_worker_succeeded(&self) -> bool {
self.worker_results.values().any(|r| r.success)
}
pub fn all_completed_workers_succeeded(&self) -> bool {
!self.worker_results.is_empty() && self.worker_results.values().all(|r| r.success)
}
pub fn completion_tick(&self) -> Option<u64> {
self.completion_tick
}
pub fn first_success(&self) -> Option<(&WorkerId, &WorkerResult)> {
self.worker_results.iter().find(|(_, r)| r.success)
}
pub fn reset(&mut self) {
self.worker_results.clear();
self.exploration_done = false;
self.exploration_exhausted = false;
self.environment_done = false;
self.final_verdict = None;
self.completion_tick = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_completion_state_default() {
let state = CompletionState::new();
assert!(!state.is_environment_done());
assert!(!state.is_exploration_done());
assert!(!state.has_verdict());
}
#[test]
fn test_record_worker_done() {
let mut state = CompletionState::new();
state.record_worker_done(WorkerId(0), true, Some("Done!".to_string()), 10);
assert!(state.is_environment_done());
assert!(state.any_worker_succeeded());
assert_eq!(state.completion_tick(), Some(10));
}
#[test]
fn test_exploration_exhausted() {
let mut state = CompletionState::new();
state.mark_exploration_done(true);
assert!(state.is_exploration_done());
assert!(state.is_exploration_exhausted());
}
#[test]
fn test_exploration_completed() {
let mut state = CompletionState::new();
state.mark_exploration_done(false);
assert!(state.is_exploration_done());
assert!(!state.is_exploration_exhausted());
}
}