harmony/harmony/src/domain/maestro/mod.rs
Ian Letourneau 49f1e56599
All checks were successful
Run Check Script / check (pull_request) Successful in -34s
add event to track progress of interprets, change a bunch of info! to debug!
2025-07-30 21:34:27 -04:00

102 lines
3.2 KiB
Rust

use std::sync::{Arc, Mutex, RwLock};
use log::{debug, info, warn};
use crate::instrumentation::{self, HarmonyEvent};
use super::{
interpret::{InterpretError, InterpretStatus, Outcome},
inventory::Inventory,
score::Score,
topology::Topology,
};
type ScoreVec<T> = Vec<Box<dyn Score<T>>>;
pub struct Maestro<T: Topology> {
inventory: Inventory,
topology: T,
scores: Arc<RwLock<ScoreVec<T>>>,
topology_preparation_result: Mutex<Option<Outcome>>,
}
impl<T: Topology> Maestro<T> {
/// Creates a bare maestro without initialization.
///
/// This should rarely be used. Most of the time Maestro::initialize should be used instead.
pub fn new_without_initialization(inventory: Inventory, topology: T) -> Self {
Self {
inventory,
topology,
scores: Arc::new(RwLock::new(Vec::new())),
topology_preparation_result: None.into(),
}
}
pub async fn initialize(inventory: Inventory, topology: T) -> Result<Self, InterpretError> {
let instance = Self::new_without_initialization(inventory, topology);
instance.prepare_topology().await?;
Ok(instance)
}
/// Ensures the associated Topology is ready for operations.
/// Delegates the readiness check and potential setup actions to the Topology.
pub async fn prepare_topology(&self) -> Result<Outcome, InterpretError> {
instrumentation::instrument(HarmonyEvent::PrepareTopologyStarted {
name: self.topology.name().to_string(),
})
.unwrap();
let outcome = self.topology.ensure_ready().await?;
instrumentation::instrument(HarmonyEvent::TopologyPrepared {
name: self.topology.name().to_string(),
outcome: outcome.clone(),
})
.unwrap();
self.topology_preparation_result
.lock()
.unwrap()
.replace(outcome.clone());
Ok(outcome)
}
pub fn register_all(&mut self, mut scores: ScoreVec<T>) {
let mut score_mut = self.scores.write().expect("Should acquire lock");
score_mut.append(&mut scores);
}
fn is_topology_initialized(&self) -> bool {
let result = self.topology_preparation_result.lock().unwrap();
if let Some(outcome) = result.as_ref() {
match outcome.status {
InterpretStatus::SUCCESS => return true,
_ => return false,
}
} else {
false
}
}
pub async fn interpret(&self, score: Box<dyn Score<T>>) -> Result<Outcome, InterpretError> {
if !self.is_topology_initialized() {
warn!(
"Launching interpret for score {} but Topology {} is not fully initialized!",
score.name(),
self.topology.name(),
);
}
debug!("Running score {score:?}");
let interpret = score.create_interpret();
debug!("Launching interpret {interpret:?}");
let result = interpret.execute(&self.inventory, &self.topology).await;
debug!("Got result {result:?}");
result
}
pub fn scores(&self) -> Arc<RwLock<ScoreVec<T>>> {
self.scores.clone()
}
}