turbovault-tools 1.3.0

MCP tools implementation using turbomcp
Documentation
//! Export tools for vault analysis data

use std::sync::Arc;
use turbovault_core::prelude::*;
use turbovault_export::{
    AnalysisReportExporter, BrokenLinkRecord, BrokenLinksExporter, HealthReportExporter,
    VaultStatsExporter, VaultStatsRecord, create_health_report,
};
use turbovault_parser::to_plain_text;
use turbovault_vault::VaultManager;

/// Export tools for vault analysis and reporting
pub struct ExportTools {
    pub manager: Arc<VaultManager>,
}

impl ExportTools {
    /// Create new export tools
    pub fn new(manager: Arc<VaultManager>) -> Self {
        Self { manager }
    }

    /// Export health report
    pub async fn export_health_report(&self, format: &str) -> Result<String> {
        let graph = self.manager.link_graph();
        let graph_read = graph.read().await;

        let stats = graph_read.stats();

        // Calculate metrics
        let total_notes = stats.total_files;
        let total_links = stats.total_links;
        let broken_links = 0; // Would need HealthAnalyzer for actual count
        let orphaned_notes = stats.orphaned_files;

        // Health score heuristic
        let health_score = if broken_links == 0 && orphaned_notes == 0 {
            100
        } else if broken_links == 0 {
            80
        } else {
            70
        };

        let report = create_health_report(
            "default",
            health_score,
            total_notes,
            total_links,
            broken_links,
            orphaned_notes,
        );

        match format {
            "json" => HealthReportExporter::to_json(&report),
            "csv" => HealthReportExporter::to_csv(&report),
            _ => Err(Error::config_error(
                "Invalid export format. Use 'json' or 'csv'".to_string(),
            )),
        }
    }

    /// Export broken links
    pub async fn export_broken_links(&self, format: &str) -> Result<String> {
        // In real implementation, would get actual broken links from HealthAnalyzer
        let links: Vec<BrokenLinkRecord> = vec![];

        match format {
            "json" => BrokenLinksExporter::to_json(&links),
            "csv" => BrokenLinksExporter::to_csv(&links),
            _ => Err(Error::config_error(
                "Invalid export format. Use 'json' or 'csv'".to_string(),
            )),
        }
    }

    /// Export vault statistics
    pub async fn export_vault_stats(&self, format: &str) -> Result<String> {
        let graph = self.manager.link_graph();
        let graph_read = graph.read().await;
        let stats = graph_read.stats();

        // Calculate readability metrics by scanning all markdown files
        let files = self.manager.scan_vault().await?;
        let mut total_words = 0usize;
        let mut total_readable_chars = 0usize;
        let mut note_count = 0usize;

        for file_path in &files {
            if !file_path.to_string_lossy().to_lowercase().ends_with(".md") {
                continue;
            }
            if let Ok(vault_file) = self.manager.parse_file(file_path).await {
                let plain_text = to_plain_text(&vault_file.content);
                total_words += plain_text.split_whitespace().count();
                total_readable_chars += plain_text.chars().count();
                note_count += 1;
            }
        }

        let avg_words_per_note = if note_count > 0 {
            total_words as f64 / note_count as f64
        } else {
            0.0
        };

        let stats_record = VaultStatsRecord {
            timestamp: chrono::Utc::now().to_rfc3339(),
            vault_name: "default".to_string(),
            total_files: stats.total_files,
            total_links: stats.total_links,
            orphaned_files: stats.orphaned_files,
            average_links_per_file: stats.average_links_per_file,
            total_words,
            total_readable_chars,
            avg_words_per_note,
        };

        match format {
            "json" => VaultStatsExporter::to_json(&stats_record),
            "csv" => VaultStatsExporter::to_csv(&stats_record),
            _ => Err(Error::config_error(
                "Invalid export format. Use 'json' or 'csv'".to_string(),
            )),
        }
    }

    /// Export full analysis report
    pub async fn export_analysis_report(&self, format: &str) -> Result<String> {
        let graph = self.manager.link_graph();
        let graph_read = graph.read().await;
        let stats = graph_read.stats();

        let total_notes = stats.total_files;
        let total_links = stats.total_links;
        let broken_links = 0;
        let orphaned_notes = stats.orphaned_files;

        let health_score = if broken_links == 0 && orphaned_notes == 0 {
            100
        } else if broken_links == 0 {
            80
        } else {
            70
        };

        let health = create_health_report(
            "default",
            health_score,
            total_notes,
            total_links,
            broken_links,
            orphaned_notes,
        );

        let analysis_report = turbovault_export::AnalysisReport {
            timestamp: chrono::Utc::now().to_rfc3339(),
            vault_name: "default".to_string(),
            health,
            broken_links_count: broken_links,
            orphaned_notes_count: orphaned_notes,
            recommendations: vec![
                "Ensure all notes are linked for better connectivity".to_string(),
                "Review and fix broken links regularly".to_string(),
            ],
        };

        match format {
            "json" => AnalysisReportExporter::to_json(&analysis_report),
            "csv" => AnalysisReportExporter::to_csv(&analysis_report),
            _ => Err(Error::config_error(
                "Invalid export format. Use 'json' or 'csv'".to_string(),
            )),
        }
    }
}