oxirs-star 0.3.1

RDF-star and SPARQL-star grammar support for quoted triples
Documentation
use std::fs;

use anyhow::Result;

use crate::cli_commands::{AnalysisResult, BenchmarkResults, ValidationResult};

pub fn print_validation_result(result: &ValidationResult) {
    if result.is_valid {
        println!("✓ Validation successful");
    } else {
        println!("✗ Validation failed");
    }

    println!("Format: {:?}", result.format);
    println!("Total triples: {}", result.triple_count);
    println!("Quoted triples: {}", result.quoted_triple_count);

    if !result.warnings.is_empty() {
        println!("\nWarnings:");
        for warning in &result.warnings {
            println!("{warning}");
        }
    }

    if !result.errors.is_empty() {
        println!("\nErrors:");
        for error in &result.errors {
            println!("{error}");
        }
    }
}

pub fn write_validation_report(result: &ValidationResult, path: &str) -> Result<()> {
    let report = serde_json::to_string_pretty(result)?;
    fs::write(path, report)?;
    println!("Validation report written to: {path}");
    Ok(())
}

pub fn print_analysis_result(analysis: &AnalysisResult) {
    println!("Analysis Results:");
    println!("================");
    println!("Format: {:?}", analysis.format);
    println!("Total triples: {}", analysis.total_triples);
    println!("Quoted triples: {}", analysis.quoted_triples);
    println!("Unique subjects: {}", analysis.subjects.len());
    println!("Unique predicates: {}", analysis.predicates.len());
    println!("Unique objects: {}", analysis.objects.len());
    println!("Max nesting depth: {}", analysis.max_nesting_depth);
    println!("Namespaces: {}", analysis.namespaces.len());

    if !analysis.namespaces.is_empty() {
        println!("\nNamespaces found:");
        for ns in &analysis.namespaces {
            println!("  {ns}");
        }
    }
}

pub fn format_analysis_report(analysis: &AnalysisResult) -> String {
    format!(
        "RDF-star Analysis Report\n\
         ========================\n\
         Format: {:?}\n\
         Total triples: {}\n\
         Quoted triples: {}\n\
         Unique subjects: {}\n\
         Unique predicates: {}\n\
         Unique objects: {}\n\
         Max nesting depth: {}\n\
         Namespaces: {}\n",
        analysis.format,
        analysis.total_triples,
        analysis.quoted_triples,
        analysis.subjects.len(),
        analysis.predicates.len(),
        analysis.objects.len(),
        analysis.max_nesting_depth,
        analysis.namespaces.len()
    )
}

pub fn print_benchmark_results(results: &BenchmarkResults) {
    let avg_parse: f64 = results
        .parse_times
        .iter()
        .map(|d| d.as_secs_f64())
        .sum::<f64>()
        / results.iterations as f64;
    let avg_serialize: f64 = results
        .serialize_times
        .iter()
        .map(|d| d.as_secs_f64())
        .sum::<f64>()
        / results.iterations as f64;

    println!("Benchmark Results:");
    println!("==================");
    println!("File size: {} bytes", results.file_size);
    println!("Iterations: {}", results.iterations);
    println!("Average parse time: {:.3}ms", avg_parse * 1000.0);
    println!("Average serialize time: {:.3}ms", avg_serialize * 1000.0);
    println!(
        "Parse throughput: {:.2} MB/s",
        (results.file_size as f64) / (1024.0 * 1024.0 * avg_parse)
    );
    println!(
        "Serialize throughput: {:.2} MB/s",
        (results.file_size as f64) / (1024.0 * 1024.0 * avg_serialize)
    );
}

pub fn display_profiling_summary(report: &crate::profiling::ProfilingReport) {
    println!("\n=== Profiling Summary ===");
    println!("Total Duration: {:?}", report.total_duration);
    println!("Total Samples: {}", report.total_samples);

    println!("\n=== Operation Statistics ===");
    for (operation, stats) in &report.operation_stats {
        println!("{operation}:");
        println!("  Count: {}", stats.count);
        println!("  Average: {:?}", stats.average_duration);
        println!("  Min: {:?}", stats.min_duration);
        println!("  Max: {:?}", stats.max_duration);
        println!("  Ops/sec: {:.2}", stats.ops_per_second);
        if let Some(bytes_per_sec) = stats.bytes_per_second {
            println!("  MB/sec: {:.2}", bytes_per_sec / 1_000_000.0);
        }
    }

    if !report.bottlenecks.is_empty() {
        println!("\n=== Performance Bottlenecks ===");
        for bottleneck in &report.bottlenecks {
            println!(
                "{}: {:.1}% - {}",
                bottleneck.operation, bottleneck.time_percentage, bottleneck.description
            );
            for suggestion in &bottleneck.suggestions {
                println!("{suggestion}");
            }
        }
    }

    if let Some(memory) = &report.memory_patterns {
        println!("\n=== Memory Analysis ===");
        println!(
            "Peak Memory: {:.2} MB",
            memory.peak_memory as f64 / 1_000_000.0
        );
        println!(
            "Average Memory: {:.2} MB",
            memory.average_memory as f64 / 1_000_000.0
        );
        println!("Efficiency Ratio: {:.2}", memory.efficiency_ratio);
        if !memory.potential_leaks.is_empty() {
            println!("Potential Issues:");
            for leak in &memory.potential_leaks {
                println!("{leak}");
            }
        }
    }
}

pub fn generate_html_report(report: &crate::profiling::ProfilingReport) -> Result<String> {
    let mut html = String::new();
    html.push_str(
        "<!DOCTYPE html><html><head><title>RDF-star Profiling Report</title></head><body>",
    );
    html.push_str("<h1>RDF-star Profiling Report</h1>");
    html.push_str(&format!(
        "<p>Generated: {}</p>",
        chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
    ));
    html.push_str(&format!(
        "<p>Total Duration: {:?}</p>",
        report.total_duration
    ));
    html.push_str(&format!("<p>Total Samples: {}</p>", report.total_samples));

    html.push_str("<h2>Operation Statistics</h2><table border='1'>");
    html.push_str("<tr><th>Operation</th><th>Count</th><th>Average</th><th>Min</th><th>Max</th><th>Ops/sec</th></tr>");
    for (operation, stats) in &report.operation_stats {
        html.push_str(&format!(
            "<tr><td>{}</td><td>{}</td><td>{:?}</td><td>{:?}</td><td>{:?}</td><td>{:.2}</td></tr>",
            operation,
            stats.count,
            stats.average_duration,
            stats.min_duration,
            stats.max_duration,
            stats.ops_per_second
        ));
    }
    html.push_str("</table>");
    html.push_str("</body></html>");
    Ok(html)
}

pub fn generate_text_report(report: &crate::profiling::ProfilingReport) -> Result<String> {
    let mut text = String::new();
    text.push_str("RDF-star Profiling Report\n");
    text.push_str("========================\n\n");
    text.push_str(&format!(
        "Generated: {}\n",
        chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC")
    ));
    text.push_str(&format!("Total Duration: {:?}\n", report.total_duration));
    text.push_str(&format!("Total Samples: {}\n\n", report.total_samples));

    text.push_str("Operation Statistics\n");
    text.push_str("-------------------\n");
    for (operation, stats) in &report.operation_stats {
        text.push_str(&format!("{operation}:\n"));
        text.push_str(&format!("  Count: {}\n", stats.count));
        text.push_str(&format!("  Average: {:?}\n", stats.average_duration));
        text.push_str(&format!("  Min: {:?}\n", stats.min_duration));
        text.push_str(&format!("  Max: {:?}\n", stats.max_duration));
        text.push_str(&format!("  Ops/sec: {:.2}\n\n", stats.ops_per_second));
    }

    if !report.bottlenecks.is_empty() {
        text.push_str("Performance Bottlenecks\n");
        text.push_str("----------------------\n");
        for bottleneck in &report.bottlenecks {
            text.push_str(&format!(
                "{}: {:.1}% - {}\n",
                bottleneck.operation, bottleneck.time_percentage, bottleneck.description
            ));
            for suggestion in &bottleneck.suggestions {
                text.push_str(&format!("{suggestion}\n"));
            }
            text.push('\n');
        }
    }

    Ok(text)
}