use anyhow::{Result, anyhow};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use regex::Regex;
use crate::config::service_registry::{ServiceRegistry, OperationConfig, ValidationRule};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ParsedOperation {
pub parsed_successfully: bool,
pub service: String,
pub operation: String,
pub confidence: f32,
pub resource_name: Option<String>,
pub extracted_params: HashMap<String, String>,
pub parsing_method: String,
pub error_message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationResult {
pub is_valid: bool,
pub error_messages: Vec<String>,
pub validated_value: Option<String>,
}
pub struct OperationContextService {
service_registry: ServiceRegistry,
operation_mappings: HashMap<String, String>,
service_patterns: HashMap<String, Vec<Regex>>,
}
impl OperationContextService {
pub async fn new() -> Result<Self> {
let service_registry = ServiceRegistry::default();
let operation_mappings = Self::load_operation_mappings();
let service_patterns = Self::load_service_patterns();
Ok(Self {
service_registry,
operation_mappings,
service_patterns,
})
}
pub async fn initialize(&self) -> Result<()> {
Ok(())
}
pub async fn shutdown(&self) -> Result<()> {
Ok(())
}
pub fn parse_operation(&self, user_request: &str) -> ParsedOperation {
let user_request = user_request.to_lowercase();
if let Some(parsed) = self.try_rag_parsing(&user_request) {
return parsed;
}
self.parse_with_keywords(&user_request)
}
fn try_rag_parsing(&self, user_request: &str) -> Option<ParsedOperation> {
None
}
fn parse_with_keywords(&self, user_request: &str) -> ParsedOperation {
if let Some((service, confidence)) = self.detect_service(user_request) {
if let Some(operation) = self.detect_operation(user_request, &service) {
let resource_name = self.extract_resource_name(user_request, &service);
let extracted_params = self.extract_parameters(user_request, &service, &operation);
return ParsedOperation {
parsed_successfully: true,
service: service.clone(),
operation: operation.clone(),
confidence,
resource_name,
extracted_params,
parsing_method: "keyword-based".to_string(),
error_message: None,
};
}
}
ParsedOperation {
parsed_successfully: false,
service: String::new(),
operation: String::new(),
confidence: 0.0,
resource_name: None,
extracted_params: HashMap::new(),
parsing_method: "failed".to_string(),
error_message: Some("Could not parse user request".to_string()),
}
}
fn detect_service(&self, user_request: &str) -> Option<(String, f32)> {
let mut best_match = None;
let mut best_confidence = 0.0;
for (service, patterns) in &self.service_patterns {
for pattern in patterns {
if pattern.is_match(user_request) {
let confidence = 0.8; if confidence > best_confidence {
best_match = Some(service.clone());
best_confidence = confidence;
}
}
}
}
best_match.map(|service| (service, best_confidence))
}
fn detect_operation(&self, user_request: &str, service: &str) -> Option<String> {
let operations = self.service_registry.get_service_operations(service);
for operation in operations {
if let Some(keywords) = self.get_operation_keywords(operation) {
for keyword in keywords {
if user_request.contains(&keyword) {
return Some(operation.to_string());
}
}
}
}
None
}
fn get_operation_keywords(&self, operation: &str) -> Option<Vec<String>> {
match operation {
"stop_instance" | "stop_db_instance" => Some(vec![
"stop".to_string(), "halt".to_string(), "shutdown".to_string(), "terminate".to_string()
]),
"start_instance" | "start_db_instance" => Some(vec![
"start".to_string(), "boot".to_string(), "launch".to_string(), "run".to_string()
]),
"delete_bucket" => Some(vec![
"delete".to_string(), "remove".to_string(), "destroy".to_string()
]),
_ => None,
}
}
fn extract_resource_name(&self, user_request: &str, service: &str) -> Option<String> {
let patterns = match service {
"aws_ec2" => vec![
Regex::new(r"instance[:\s]+([a-zA-Z0-9\-_]+)").unwrap(),
Regex::new(r"i-[0-9a-f]+").unwrap(),
],
"aws_rds" => vec![
Regex::new(r"database[:\s]+([a-zA-Z0-9\-_]+)").unwrap(),
Regex::new(r"db[:\s]+([a-zA-Z0-9\-_]+)").unwrap(),
],
"aws_s3" => vec![
Regex::new(r"bucket[:\s]+([a-zA-Z0-9\-_\.]+)").unwrap(),
],
_ => vec![],
};
for pattern in patterns {
if let Some(captures) = pattern.captures(user_request) {
if let Some(name) = captures.get(1) {
return Some(name.as_str().to_string());
} else if let Some(name) = captures.get(0) {
return Some(name.as_str().to_string());
}
}
}
None
}
fn extract_parameters(&self, user_request: &str, service: &str, operation: &str) -> HashMap<String, String> {
let mut params = HashMap::new();
if let Some(region) = self.extract_region(user_request) {
params.insert("region".to_string(), region);
}
if user_request.contains("force") || user_request.contains("forcefully") {
params.insert("force".to_string(), "true".to_string());
}
match service {
"aws_rds" => {
if user_request.contains("snapshot") {
if let Some(snapshot_id) = self.extract_snapshot_id(user_request) {
params.insert("snapshot_id".to_string(), snapshot_id);
}
}
},
_ => {}
}
params
}
fn extract_region(&self, user_request: &str) -> Option<String> {
let region_patterns = vec![
Regex::new(r"in\s+(us-[a-z]+-\d+)").unwrap(),
Regex::new(r"region[:\s]+(us-[a-z]+-\d+)").unwrap(),
Regex::new(r"(eu-[a-z]+-\d+)").unwrap(),
Regex::new(r"(ap-[a-z]+-\d+)").unwrap(),
];
for pattern in region_patterns {
if let Some(captures) = pattern.captures(user_request) {
if let Some(region) = captures.get(1) {
return Some(region.as_str().to_string());
}
}
}
None
}
fn extract_snapshot_id(&self, user_request: &str) -> Option<String> {
let snapshot_pattern = Regex::new(r"snapshot[:\s]+([a-zA-Z0-9\-_]+)").unwrap();
if let Some(captures) = snapshot_pattern.captures(user_request) {
if let Some(snapshot_id) = captures.get(1) {
return Some(snapshot_id.as_str().to_string());
}
}
None
}
pub fn get_operation_config(&self, service: &str, operation: &str) -> Option<&OperationConfig> {
self.service_registry.get_operation_config(service, operation)
}
pub fn validate_parameter(
&self,
service: &str,
operation: &str,
param_name: &str,
value: &str,
) -> ValidationResult {
if let Some(config) = self.get_operation_config(service, operation) {
if let Some(validation_rule) = config.validation_rules.get(param_name) {
return self.apply_validation_rule(validation_rule, value);
}
}
ValidationResult {
is_valid: !value.trim().is_empty(),
error_messages: if value.trim().is_empty() {
vec!["Value cannot be empty".to_string()]
} else {
vec![]
},
validated_value: Some(value.to_string()),
}
}
fn apply_validation_rule(&self, rule: &ValidationRule, value: &str) -> ValidationResult {
let mut errors = Vec::new();
match rule.rule_type.as_str() {
"string" => {
if let Some(min_len) = rule.min_length {
if value.len() < min_len {
errors.push(format!("Value must be at least {} characters long", min_len));
}
}
if let Some(max_len) = rule.max_length {
if value.len() > max_len {
errors.push(format!("Value must be no more than {} characters long", max_len));
}
}
},
"boolean" => {
if !matches!(value, "true" | "false" | "1" | "0" | "yes" | "no") {
errors.push("Value must be a boolean (true/false)".to_string());
}
},
_ => {}
}
if let Some(pattern_str) = &rule.pattern {
if let Ok(pattern) = Regex::new(pattern_str) {
if !pattern.is_match(value) {
errors.push(format!("Value does not match required pattern: {}", pattern_str));
}
}
}
if let Some(enum_values) = &rule.enum_values {
if !enum_values.contains(&value.to_string()) {
errors.push(format!("Value must be one of: {}", enum_values.join(", ")));
}
}
let is_valid = errors.is_empty();
ValidationResult {
is_valid,
error_messages: errors,
validated_value: if is_valid { Some(value.to_string()) } else { None },
}
}
pub fn is_data_confidential(&self, service: &str, operation: &str, field_name: &str) -> bool {
if let Some(config) = self.get_operation_config(service, operation) {
config.confidential_data.contains(&field_name.to_string())
} else {
false
}
}
pub fn is_data_non_confidential(&self, service: &str, operation: &str, field_name: &str) -> bool {
if let Some(config) = self.get_operation_config(service, operation) {
config.non_confidential_data.contains(&field_name.to_string())
} else {
false
}
}
pub fn get_required_parameters(&self, service: &str, operation: &str) -> Vec<String> {
if let Some(config) = self.get_operation_config(service, operation) {
config.required_params.clone()
} else {
vec![]
}
}
pub fn get_optional_parameters(&self, service: &str, operation: &str) -> Vec<String> {
if let Some(config) = self.get_operation_config(service, operation) {
config.optional_params.clone()
} else {
vec![]
}
}
pub fn get_all_services(&self) -> Vec<String> {
self.service_registry.get_all_services()
.into_iter()
.map(|s| s.to_string())
.collect()
}
pub fn get_service_operations(&self, service: &str) -> Vec<String> {
self.service_registry.get_service_operations(service)
.into_iter()
.map(|op| op.to_string())
.collect()
}
fn load_operation_mappings() -> HashMap<String, String> {
let mut mappings = HashMap::new();
mappings.insert("halt".to_string(), "stop".to_string());
mappings.insert("shutdown".to_string(), "stop".to_string());
mappings.insert("terminate".to_string(), "stop".to_string());
mappings.insert("boot".to_string(), "start".to_string());
mappings.insert("launch".to_string(), "start".to_string());
mappings.insert("run".to_string(), "start".to_string());
mappings.insert("remove".to_string(), "delete".to_string());
mappings.insert("destroy".to_string(), "delete".to_string());
mappings
}
fn load_service_patterns() -> HashMap<String, Vec<Regex>> {
let mut patterns = HashMap::new();
patterns.insert("aws_ec2".to_string(), vec![
Regex::new(r"ec2|instance").unwrap(),
Regex::new(r"virtual machine|vm").unwrap(),
Regex::new(r"compute|server").unwrap(),
]);
patterns.insert("aws_rds".to_string(), vec![
Regex::new(r"rds|database|db").unwrap(),
Regex::new(r"mysql|postgres|oracle").unwrap(),
Regex::new(r"sql server").unwrap(),
]);
patterns.insert("aws_s3".to_string(), vec![
Regex::new(r"s3|bucket").unwrap(),
Regex::new(r"storage|object").unwrap(),
Regex::new(r"file|blob").unwrap(),
]);
patterns
}
pub fn get_parsing_summary(&self, parsed: &ParsedOperation) -> String {
if parsed.parsed_successfully {
format!(
"Successfully parsed '{}' operation for '{}' service (confidence: {:.1}%) using {} method",
parsed.operation, parsed.service, parsed.confidence * 100.0, parsed.parsing_method
)
} else {
format!(
"Failed to parse request: {}",
parsed.error_message.as_ref().unwrap_or(&"Unknown error".to_string())
)
}
}
}