102 lines
3.2 KiB
Rust
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()
|
|
}
|
|
}
|