cargo-mate 1.7.6

Rust development companion that enhances cargo with intelligent workflows, state management, performance optimization, and comprehensive project monitoring.
Documentation
use anyhow::{Context, Result};
use colored::*;
use regex::Regex;
use reqwest;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct SmartError {
    pub code: String,
    pub message: String,
    pub file: PathBuf,
    pub line: usize,
    pub column: usize,
    pub suggestion: Option<String>,
    pub fix_command: Option<String>,
    pub explanation: Option<String>,
    pub related_docs: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
struct ErrorPattern {
    code: String,
    pattern: String,
    suggestion: String,
    fix_command: Option<String>,
    docs_url: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
struct ErrorDatabase {
    patterns: Vec<ErrorPattern>,
    common_fixes: HashMap<String, Vec<String>>,
    learning_data: HashMap<String, FixHistory>,
}
#[derive(Debug, Serialize, Deserialize)]
struct FixHistory {
    successful_fixes: Vec<String>,
    failed_attempts: Vec<String>,
    frequency: usize,
}
pub struct SmartParser {
    error_db: ErrorDatabase,
    db_file: PathBuf,
    online_search: bool,
}
impl SmartParser {
    pub fn new() -> Result<Self> {
        let db_file = dirs::home_dir()
            .context("Could not find home directory")?
            .join(".shipwreck")
            .join("error_db.json");
        let error_db = if db_file.exists() {
            let content = fs::read_to_string(&db_file)?;
            serde_json::from_str(&content)?
        } else {
            Self::create_default_database()
        };
        Ok(Self {
            error_db,
            db_file,
            online_search: true,
        })
    }
    fn create_default_database() -> ErrorDatabase {
        let patterns = vec![
            ErrorPattern { code : "E0308".to_string(), pattern : "mismatched types"
            .to_string(), suggestion :
            "Check the expected and found types. You may need to convert types using methods like .to_string(), .into(), or as_ref()"
            .to_string(), fix_command : Some("cargo fix --allow-dirty".to_string()),
            docs_url : Some("https://doc.rust-lang.org/error-index.html#E0308"
            .to_string()), }, ErrorPattern { code : "E0382".to_string(), pattern :
            "borrow of moved value".to_string(), suggestion :
            "The value has been moved. Consider cloning it, using a reference, or restructuring ownership"
            .to_string(), fix_command : None, docs_url :
            Some("https://doc.rust-lang.org/error-index.html#E0382".to_string()), },
            ErrorPattern { code : "E0499".to_string(), pattern :
            "cannot borrow .* as mutable more than once".to_string(), suggestion :
            "You have multiple mutable borrows. Consider using RefCell for interior mutability or restructuring your code"
            .to_string(), fix_command : None, docs_url :
            Some("https://doc.rust-lang.org/error-index.html#E0499".to_string()), },
            ErrorPattern { code : "E0277".to_string(), pattern :
            "the trait bound .* is not satisfied".to_string(), suggestion :
            "The required trait is not implemented. Check if you need to import the trait or implement it for your type"
            .to_string(), fix_command : None, docs_url :
            Some("https://doc.rust-lang.org/error-index.html#E0277".to_string()), },
            ErrorPattern { code : "E0433".to_string(), pattern : "failed to resolve"
            .to_string(), suggestion :
            "Module or item not found. Check your imports and module declarations"
            .to_string(), fix_command : Some("cargo check".to_string()), docs_url :
            Some("https://doc.rust-lang.org/error-index.html#E0433".to_string()), },
        ];
        let mut common_fixes = HashMap::new();
        common_fixes
            .insert(
                "lifetime".to_string(),
                vec![
                    "Add lifetime parameters to your struct/function".to_string(),
                    "Use 'static lifetime if the value needs to live for the entire program"
                    .to_string(), "Consider using Arc<T> or Rc<T> for shared ownership"
                    .to_string(),
                ],
            );
        common_fixes
            .insert(
                "async".to_string(),
                vec![
                    "Make sure you're using .await on async functions".to_string(),
                    "Check if your function needs to be async".to_string(),
                    "Consider using tokio::spawn or async blocks".to_string(),
                ],
            );
        common_fixes
            .insert(
                "iterator".to_string(),
                vec![
                    "Check if you need to call .collect() to consume the iterator"
                    .to_string(),
                    "Make sure you're using the right iterator method (map, filter, fold, etc.)"
                    .to_string(),
                    "Consider using .iter() or .into_iter() based on ownership needs"
                    .to_string(),
                ],
            );
        ErrorDatabase {
            patterns,
            common_fixes,
            learning_data: HashMap::new(),
        }
    }
    pub fn parse_error(&mut self, error_text: &str) -> SmartError {
        let code = self.extract_error_code(error_text);
        let (file, line, column) = self.extract_location(error_text);
        let message = self.extract_message(error_text);
        let suggestion = self.get_suggestion(&code, &message);
        let fix_command = self.get_fix_command(&code);
        let explanation = self.get_explanation(&code, &message);
        let related_docs = self.get_related_docs(&code);
        self.learn_from_error(&code, &message);
        SmartError {
            code,
            message,
            file,
            line,
            column,
            suggestion,
            fix_command,
            explanation,
            related_docs,
        }
    }
    fn extract_error_code(&self, text: &str) -> String {
        let re = Regex::new(r"error\[([A-Z0-9]+)\]").unwrap();
        re.captures(text)
            .and_then(|cap| cap.get(1))
            .map(|m| m.as_str().to_string())
            .unwrap_or_else(|| "unknown".to_string())
    }
    fn extract_location(&self, text: &str) -> (PathBuf, usize, usize) {
        let re = Regex::new(r"(\S+\.rs):(\d+):(\d+)").unwrap();
        if let Some(cap) = re.captures(text) {
            let file = PathBuf::from(cap.get(1).unwrap().as_str());
            let line = cap.get(2).unwrap().as_str().parse().unwrap_or(0);
            let column = cap.get(3).unwrap().as_str().parse().unwrap_or(0);
            (file, line, column)
        } else {
            (PathBuf::from("unknown"), 0, 0)
        }
    }
    fn extract_message(&self, text: &str) -> String {
        let lines: Vec<&str> = text.lines().collect();
        if let Some(error_line) = lines.iter().find(|l| l.contains("error")) {
            let parts: Vec<&str> = error_line.split(':').collect();
            if parts.len() > 1 {
                return parts[1..].join(":").trim().to_string();
            }
        }
        text.to_string()
    }
    fn get_suggestion(&self, code: &str, message: &str) -> Option<String> {
        for pattern in &self.error_db.patterns {
            if pattern.code == code {
                return Some(pattern.suggestion.clone());
            }
        }
        if message.contains("lifetime") {
            if let Some(fixes) = self.error_db.common_fixes.get("lifetime") {
                return Some(fixes.join("\n"));
            }
        }
        if message.contains("async") || message.contains("await") {
            if let Some(fixes) = self.error_db.common_fixes.get("async") {
                return Some(fixes.join("\n"));
            }
        }
        if self.online_search {
            self.search_online_suggestion(code, message).ok()
        } else {
            None
        }
    }
    fn get_fix_command(&self, code: &str) -> Option<String> {
        self.error_db
            .patterns
            .iter()
            .find(|p| p.code == code)
            .and_then(|p| p.fix_command.clone())
    }
    fn get_explanation(&self, code: &str, message: &str) -> Option<String> {
        let base_explanation = match code {
            "E0308" => {
                "Type mismatch occurs when Rust expects one type but finds another."
            }
            "E0382" => {
                "Ownership has been transferred. In Rust, each value has a single owner."
            }
            "E0499" => {
                "Rust's borrowing rules prevent multiple mutable references to prevent data races."
            }
            "E0277" => {
                "A trait bound was not satisfied. The type doesn't implement the required trait."
            }
            "E0433" => "Failed to resolve a path to a module, type, or function.",
            _ => return None,
        };
        Some(format!("{}\n\nContext: {}", base_explanation, message))
    }
    fn get_related_docs(&self, code: &str) -> Vec<String> {
        let mut docs = vec![
            format!("https://doc.rust-lang.org/error-index.html#{}", code),
        ];
        if let Some(pattern) = self.error_db.patterns.iter().find(|p| p.code == code) {
            if let Some(ref url) = pattern.docs_url {
                docs.push(url.clone());
            }
        }
        docs.push("https://doc.rust-lang.org/book/".to_string());
        docs
    }
    fn search_online_suggestion(&self, code: &str, message: &str) -> Result<String> {
        Ok(
            format!(
                "💡 Search for solutions:\n  - Stack Overflow: rust {} {}\n  - Rust Forum: https://users.rust-lang.org/",
                code, message.split_whitespace().take(5).collect::< Vec < _ >> ()
                .join(" ")
            ),
        )
    }
    fn learn_from_error(&mut self, code: &str, _message: &str) {
        let entry = self
            .error_db
            .learning_data
            .entry(code.to_string())
            .or_insert(FixHistory {
                successful_fixes: Vec::new(),
                failed_attempts: Vec::new(),
                frequency: 0,
            });
        entry.frequency += 1;
        let _ = self.save_database();
    }
    pub fn record_fix(&mut self, code: &str, fix: &str, successful: bool) -> Result<()> {
        let entry = self
            .error_db
            .learning_data
            .entry(code.to_string())
            .or_insert(FixHistory {
                successful_fixes: Vec::new(),
                failed_attempts: Vec::new(),
                frequency: 0,
            });
        if successful {
            entry.successful_fixes.push(fix.to_string());
        } else {
            entry.failed_attempts.push(fix.to_string());
        }
        self.save_database()?;
        Ok(())
    }
    pub fn display_smart_error(&self, error: &SmartError) {
        println!(
            "{}", format!("═══ Error {} ═══", error.code) .red().bold()
        );
        println!(
            "📁 {}:{}:{}", error.file.display(), error.line.to_string().yellow(), error
            .column
        );
        println!("📝 {}", error.message.white());
        if let Some(ref suggestion) = error.suggestion {
            println!("\n💡 {}", "Suggestion:".green().bold());
            for line in suggestion.lines() {
                println!("   {}", line);
            }
        }
        if let Some(ref cmd) = error.fix_command {
            println!("\n🔧 {}", "Quick fix:".cyan().bold());
            println!("   {}", cmd.cyan());
        }
        if let Some(ref explanation) = error.explanation {
            println!("\n📖 {}", "Explanation:".blue().bold());
            for line in explanation.lines() {
                println!("   {}", line.dimmed());
            }
        }
        if !error.related_docs.is_empty() {
            println!("\n📚 {}", "Related Documentation:".magenta().bold());
            for doc in &error.related_docs {
                println!("{}", doc.underline());
            }
        }
        println!("{}", "".repeat(50).red());
    }
    pub fn suggest_learning_path(&self, errors: &[SmartError]) {
        let mut error_categories: HashMap<String, usize> = HashMap::new();
        for error in errors {
            let category = self.categorize_error(&error.code);
            *error_categories.entry(category).or_insert(0) += 1;
        }
        if error_categories.is_empty() {
            return;
        }
        println!("{}", "📚 Learning Path Recommendation".green().bold());
        println!("Based on your errors, consider studying:");
        let mut categories: Vec<_> = error_categories.into_iter().collect();
        categories.sort_by(|a, b| b.1.cmp(&a.1));
        for (category, count) in categories.iter().take(3) {
            let resources = self.get_learning_resources(category);
            println!("\n{} ({} errors)", category.cyan(), count);
            for resource in resources {
                println!("{}", resource);
            }
        }
    }
    fn categorize_error(&self, code: &str) -> String {
        match code {
            c if c.starts_with("E03") => "Ownership & Borrowing".to_string(),
            c if c.starts_with("E04") => "Pattern Matching".to_string(),
            c if c.starts_with("E05") => "Traits & Generics".to_string(),
            c if c.starts_with("E06") => "Modules & Visibility".to_string(),
            c if c.starts_with("E07") => "Async & Concurrency".to_string(),
            _ => "General Rust".to_string(),
        }
    }
    fn get_learning_resources(&self, category: &str) -> Vec<String> {
        match category {
            "Ownership & Borrowing" => {
                vec![
                    "The Rust Book - Chapter 4: Understanding Ownership".to_string(),
                    "Rust by Example - Ownership section".to_string(),
                    "Video: 'Rust Ownership Explained' by No Boilerplate".to_string(),
                ]
            }
            "Traits & Generics" => {
                vec![
                    "The Rust Book - Chapter 10: Generic Types, Traits".to_string(),
                    "Rust by Example - Traits section".to_string(),
                    "Blog: 'Rust Traits: A Deep Dive'".to_string(),
                ]
            }
            _ => {
                vec![
                    "The Rust Programming Language Book".to_string(), "Rust by Example"
                    .to_string(), "Rustlings exercises".to_string(),
                ]
            }
        }
    }
    fn save_database(&self) -> Result<()> {
        let json = serde_json::to_string_pretty(&self.error_db)?;
        fs::write(&self.db_file, json)?;
        Ok(())
    }
}