use anyhow::Result;
use std::collections::HashSet;
use super::catalog::{Environment, EnvironmentCatalog};
use super::profile::RobotProfile;
#[derive(Debug, Clone)]
pub struct EnvironmentMatch {
pub environment: Environment,
pub score: i32,
pub reasons: Vec<String>,
}
pub struct EnvironmentSelector {
catalog: EnvironmentCatalog,
}
impl EnvironmentSelector {
pub fn new() -> Result<Self> {
let catalog = EnvironmentCatalog::load_default()?;
Ok(Self { catalog })
}
pub fn with_catalog(catalog: EnvironmentCatalog) -> Self {
Self { catalog }
}
pub fn select_environments(&self, profile: &RobotProfile, max_envs: usize) -> Result<Vec<EnvironmentMatch>> {
let mut matches: Vec<EnvironmentMatch> = self
.catalog
.environments
.iter()
.map(|env| self.score_environment(env, profile))
.filter(|m| m.score > 0) .collect();
matches.sort_by_key(|m| -m.score);
Ok(matches.into_iter().take(max_envs).collect())
}
fn score_environment(&self, env: &Environment, profile: &RobotProfile) -> EnvironmentMatch {
let mut score = 0;
let mut reasons = Vec::new();
for task_node in &profile.task_nodes {
if env.task_nodes.contains(&task_node.name) {
score += 20;
reasons.push(format!("Task node match: {}", task_node.name));
}
}
let node_tags: HashSet<String> = profile.task_nodes.iter().flat_map(|n| n.tags.iter().cloned()).collect();
for tag in &node_tags {
if env.tags.contains(tag) {
score += 10;
reasons.push(format!("Tag match: {}", tag));
}
}
let has_required_sensors = env.is_compatible_with_sensors(&profile.sensors);
if has_required_sensors {
score += 5;
reasons.push("All required sensors present".to_string());
} else {
score -= 50; let missing: Vec<_> = env
.required_sensors
.iter()
.filter(|s| !profile.sensors.contains(s))
.collect();
reasons.push(format!(
"Missing required sensors: {}",
missing.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(", ")
));
}
if env.is_compatible_with_platform(&profile.platform) {
score += 3;
reasons.push(format!("Platform match: {}", profile.platform));
}
if env.difficulty == profile.difficulty_preference {
score += 2;
reasons.push(format!("Difficulty match: {}", env.difficulty));
}
for capability in &profile.capabilities {
if env.tags.contains(capability) {
score += 1;
reasons.push(format!("Capability match: {}", capability));
}
}
EnvironmentMatch {
environment: env.clone(),
score,
reasons,
}
}
pub fn catalog(&self) -> &EnvironmentCatalog {
&self.catalog
}
}