use std::collections::HashMap;
use traverse_contracts::{CapabilityContract, ExecutionTarget, ServiceType};
pub struct RuntimeSnapshot {
pub target_loads: HashMap<ExecutionTarget, f32>,
}
pub struct PlacementRequest {
pub capability_id: String,
pub target_hint: Option<ExecutionTarget>,
pub runtime_snapshot: RuntimeSnapshot,
}
#[derive(Debug)]
pub struct PlacementDecision {
pub target: ExecutionTarget,
pub reason: PlacementReason,
pub confidence: PlacementConfidence,
}
#[derive(Debug)]
pub enum PlacementReason {
CallerHintAccepted,
ContractConstrained,
HeuristicSelected,
}
#[derive(Debug)]
pub enum PlacementConfidence {
High,
Medium,
Low,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PlacementError {
NoEligibleTarget,
}
pub struct PlacementConstraintEvaluator;
impl PlacementConstraintEvaluator {
pub fn evaluate(
&self,
request: &PlacementRequest,
contract: &CapabilityContract,
) -> Result<PlacementDecision, PlacementError> {
if let Some(ref hint) = request.target_hint
&& contract.permitted_targets.contains(hint)
{
let load = load_for(&request.runtime_snapshot, hint);
return Ok(PlacementDecision {
target: hint.clone(),
reason: PlacementReason::CallerHintAccepted,
confidence: confidence_for(load),
});
}
let mut eligible: Vec<ExecutionTarget> = contract
.permitted_targets
.iter()
.filter(|t| {
!(contract.service_type == ServiceType::Stateful && **t == ExecutionTarget::Browser)
})
.cloned()
.collect();
eligible.retain(|t| load_for(&request.runtime_snapshot, t) <= 0.9);
if eligible.is_empty() {
return Err(PlacementError::NoEligibleTarget);
}
let selected = eligible
.into_iter()
.min_by(|a, b| {
let la = load_for(&request.runtime_snapshot, a);
let lb = load_for(&request.runtime_snapshot, b);
la.partial_cmp(&lb)
.unwrap_or(std::cmp::Ordering::Equal)
.then_with(|| format!("{a:?}").cmp(&format!("{b:?}")))
})
.ok_or(PlacementError::NoEligibleTarget)?;
let load = load_for(&request.runtime_snapshot, &selected);
Ok(PlacementDecision {
target: selected,
reason: PlacementReason::HeuristicSelected,
confidence: confidence_for(load),
})
}
}
fn load_for(snapshot: &RuntimeSnapshot, target: &ExecutionTarget) -> f32 {
snapshot.target_loads.get(target).copied().unwrap_or(0.0)
}
fn confidence_for(load: f32) -> PlacementConfidence {
if load < 0.5 {
PlacementConfidence::High
} else if load < 0.75 {
PlacementConfidence::Medium
} else {
PlacementConfidence::Low
}
}