Jesús Pérez 09a97ac8f5
chore: update platform submodule to monorepo crates structure
Platform restructured into crates/, added AI service and detector,
       migrated control-center-ui to Leptos 0.8
2026-01-08 21:32:59 +00:00

206 lines
7.2 KiB
Rust

/// Inference engine
/// Converts technology detections into infrastructure requirements
/// Uses rule-based system to suggest taskservs based on what was detected
use crate::models::{Detection, Requirement, Technology};
/// Inference rule: detect pattern → infer requirement
pub struct InferenceRule {
pub name: &'static str,
pub condition: fn(&[Detection]) -> bool,
pub infer: fn(&[Detection]) -> Vec<Requirement>,
}
/// Main inference engine
pub struct InferenceEngine {
rules: Vec<InferenceRule>,
}
impl InferenceEngine {
pub fn new(rules: Vec<InferenceRule>) -> Self {
Self { rules }
}
/// Create engine with default rules
pub fn with_default_rules() -> Self {
let rules = vec![
// Rule 1: Node.js + Express → Redis (API caching)
InferenceRule {
name: "NodeJS API recommends Redis",
condition: |detections| {
detections
.iter()
.any(|d| d.technology == Technology::NodeJs)
&& detections
.iter()
.any(|d| d.technology == Technology::Express)
},
infer: |_| {
vec![Requirement::new(
"redis",
"Express.js APIs benefit from caching layer",
0.85,
false,
)]
},
},
// Rule 2: Any database detected → needs backups
InferenceRule {
name: "Databases need backup strategy",
condition: |detections| {
detections.iter().any(|d| {
matches!(
d.technology,
Technology::Postgres | Technology::Mysql | Technology::Mongodb
)
})
},
infer: |detections| {
detections
.iter()
.filter(|d| {
matches!(
d.technology,
Technology::Postgres | Technology::Mysql | Technology::Mongodb
)
})
.map(|d| {
let taskserv = match d.technology {
Technology::Postgres => "postgres-backup",
Technology::Mysql => "mysql-backup",
Technology::Mongodb => "mongodb-backup",
_ => "backup",
};
Requirement::new(
taskserv,
"Production databases require backup strategy",
0.90,
false,
)
})
.collect()
},
},
// Rule 3: Containerized apps need reverse proxy
InferenceRule {
name: "Docker apps need reverse proxy",
condition: |detections| {
detections
.iter()
.any(|d| d.technology == Technology::Docker)
},
infer: |_| {
vec![Requirement::new(
"nginx",
"Containerized applications should run behind reverse proxy",
0.75,
false,
)]
},
},
// Rule 4: Any language detected → needs runtime
InferenceRule {
name: "Languages need runtime",
condition: |detections| {
detections.iter().any(|d| {
matches!(
d.technology,
Technology::NodeJs | Technology::Python | Technology::Rust
)
})
},
infer: |detections| {
let lang = detections.iter().find(|d| {
matches!(
d.technology,
Technology::NodeJs | Technology::Python | Technology::Rust
)
});
match lang.map(|d| d.technology) {
Some(Technology::NodeJs) => vec![], // Already detected
Some(Technology::Python) => vec![], // Already detected
Some(Technology::Rust) => vec![], // Already detected
_ => vec![],
}
},
},
// Rule 5: PostgreSQL detected → add monitoring
InferenceRule {
name: "PostgreSQL should have monitoring",
condition: |detections| {
detections
.iter()
.any(|d| d.technology == Technology::Postgres)
},
infer: |_| {
vec![Requirement::new(
"pg-monitoring",
"Monitor PostgreSQL performance in production",
0.70,
false,
)
.with_min_version("14.0".to_string())]
},
},
];
Self::new(rules)
}
/// Infer requirements from detections
pub fn infer_requirements(&self, detections: &[Detection]) -> Vec<Requirement> {
let mut requirements = Vec::new();
for rule in &self.rules {
if (rule.condition)(detections) {
let inferred = (rule.infer)(detections);
requirements.extend(inferred);
}
}
// Deduplicate and keep highest confidence
requirements.sort_by(|a, b| b.confidence.partial_cmp(&a.confidence).unwrap());
requirements.dedup_by(|a, b| a.taskserv == b.taskserv);
requirements
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nodejs_express_recommends_redis() {
let detections = vec![
Detection::new(Technology::NodeJs),
Detection::new(Technology::Express),
];
let engine = InferenceEngine::with_default_rules();
let requirements = engine.infer_requirements(&detections);
assert!(requirements.iter().any(|r| r.taskserv == "redis"));
}
#[test]
fn test_docker_recommends_nginx() {
let detections = vec![Detection::new(Technology::Docker)];
let engine = InferenceEngine::with_default_rules();
let requirements = engine.infer_requirements(&detections);
assert!(requirements.iter().any(|r| r.taskserv == "nginx"));
}
#[test]
fn test_postgres_detected() {
let detections = vec![Detection::new(Technology::Postgres)];
let engine = InferenceEngine::with_default_rules();
let requirements = engine.infer_requirements(&detections);
assert!(!requirements.is_empty());
}
}