rag-module 0.6.7

Enterprise RAG module with chat context storage, vector search, session management, and model downloading. Rust implementation with Node.js compatibility.
//! VPC Service Parser
//! 
//! Parses AWS VPC service data and generates searchable documents

use crate::services::aws_estate_service::AwsServiceParser;
use crate::types::Document;
use anyhow::{Result, anyhow};
use serde_json::{Value, json};
use std::collections::HashMap;
use tracing::{debug, warn};
use indexmap::IndexMap;
use uuid::Uuid;
use async_trait::async_trait;

pub struct VpcParser;

impl VpcParser {
    pub fn new() -> Self {
        Self
    }

    /// Create base metadata for AWS resources
    fn create_base_metadata(
        account_id: &str, 
        service: &str, 
        resource_type: &str,
        region: Option<&str>
    ) -> IndexMap<String, Value> {
        let mut metadata = IndexMap::new();
        metadata.insert("account_id".to_string(), json!(account_id));
        metadata.insert("service".to_string(), json!(service));
        metadata.insert("resource_type".to_string(), json!(resource_type));
        metadata.insert("cloud_provider".to_string(), json!("aws"));
        metadata.insert("document_type".to_string(), json!("aws_estate"));
        metadata.insert("last_synced".to_string(), json!(chrono::Utc::now().timestamp()));
        
        if let Some(region_val) = region {
            metadata.insert("region".to_string(), json!(region_val));
        }
        
        metadata
    }

    /// Generate AWS ARN for resources
    fn generate_arn(
        service: &str, 
        region: &str, 
        account_id: &str, 
        resource_type: &str, 
        resource_id: &str
    ) -> String {
        format!("arn:aws:{}:{}:{}:{}:{}", service, region, account_id, resource_type, resource_id)
    }

    /// Parse VPCs from the service data
    fn parse_vpcs(&self, account_id: &str, vpcs: &[Value]) -> Result<Vec<Document>> {
        let mut documents = Vec::new();
        
        for vpc in vpcs {
            let vpc_id = vpc["vpc_id"]
                .as_str()
                .unwrap_or("unknown");
            let region = vpc["region"]
                .as_str()
                .unwrap_or("us-east-1");
            let cidr_block = vpc["cidr_block"]
                .as_str()
                .unwrap_or("unknown");
            let state = vpc["state"]
                .as_str()
                .unwrap_or("unknown");
            let is_default = vpc["is_default"]
                .as_bool()
                .unwrap_or(false);
            
            // Generate ARN for the VPC
            let doc_id = Self::generate_arn(
                "ec2",  // VPCs are under EC2 service in AWS
                region,
                account_id,
                "vpc",
                vpc_id,
            );
            
            // Create searchable content
            let content = format!(
                "VPC {} in region {} - CIDR: {} - State: {} - Default VPC: {}",
                vpc_id, region, cidr_block, state, if is_default { "Yes" } else { "No" }
            );
            
            // Create base metadata
            let mut metadata = Self::create_base_metadata(
                account_id,
                "vpc",
                "vpc",
                Some(region)
            );
            
            // Add VPC-specific metadata
            metadata.insert("vpc_id".to_string(), json!(vpc_id));
            metadata.insert("cidr_block".to_string(), json!(cidr_block));
            metadata.insert("state".to_string(), json!(state));
            metadata.insert("is_default".to_string(), json!(is_default));
            
            let document = Document::new(
                doc_id,
                content,
            ).with_metadata(metadata);
            
            documents.push(document);
            debug!("✅ Created VPC document for: {}", vpc_id);
        }
        
        Ok(documents)
    }
}

#[async_trait]
impl AwsServiceParser for VpcParser {
    fn service_name(&self) -> &str {
        "vpc"
    }
    
    fn can_parse(&self, service_data: &Value) -> bool {
        // Check if this looks like VPC data
        let can_parse = service_data.is_object() && (
            service_data.get("vpcs").is_some() ||
            service_data.get("total_vpcs").is_some()
        );
        can_parse
    }
    
    async fn parse(&self, account_id: &str, service_data: &Value) -> Result<Vec<Document>> {
        debug!("🔍 VPC parser processing data for account: {}", account_id);
        
        let mut documents = Vec::new();
        
        // Parse VPCs
        if let Some(vpcs) = service_data.get("vpcs").and_then(|v| v.as_array()) {
            if !vpcs.is_empty() {
                let mut vpc_docs = self.parse_vpcs(account_id, vpcs)?;
                documents.append(&mut vpc_docs);
                debug!("✅ Parsed {} VPCs", vpcs.len());
            }
        }
        
        if documents.is_empty() {
            warn!("🟡 VPC parser found no parseable data");
        } else {
            debug!("🎉 VPC parser generated {} documents", documents.len());
        }
        
        Ok(documents)
    }
    
    fn get_data_schema(&self) -> Option<Value> {
        Some(json!({
            "type": "object",
            "properties": {
                "vpcs": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "required": ["vpc_id", "region", "cidr_block", "state"],
                        "properties": {
                            "vpc_id": { "type": "string" },
                            "region": { "type": "string" },
                            "cidr_block": { "type": "string" },
                            "state": { "type": "string" },
                            "is_default": { "type": "boolean" }
                        }
                    }
                },
                "total_vpcs": { "type": "number" },
                "permissions": { "type": "object" }
            }
        }))
    }
}