diagprint 0.8.1

A Rust diagnostics lifecycle framework for structured diagnostics, rendering, remediation, CI, editors, and telemetry.
Documentation
use diagprint::{
    Applicability, DocumentationLink, Edit, Fixer, Reporter, SuggestedCommand, Suggestion,
    TextRange,
};
use std::{env, fs, path::PathBuf};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let demo_dir = PathBuf::from("target/diagprint-demo");
    let demo_file = demo_dir.join("Cargo.toml");

    fs::create_dir_all(&demo_dir)?;

    let original = "chrono = { version = \"0.4\", features = [\"clock\"] }";

    let replacement = "chrono = { version = \"0.4\", features = [\"clock\", \"serde\"] }";

    fs::write(&demo_file, format!("{original}\n"))?;

    let reporter = Reporter::builder()
        .application("diagprint-intelligence")
        .show_metadata(true)
        .width(88)
        .build()?;

    let diagnostic = reporter
        .error("chrono::DateTime cannot be serialized")
        .code("CARGO-001")
        .suggestion(
            Suggestion::new("Enable chrono's serde feature")
                .explanation(
                    "The chrono crate only provides serde implementations \
                     when its `serde` feature is enabled.",
                )
                .applicability(Applicability::MachineApplicable)
                .documentation(DocumentationLink::docs_rs("chrono", "latest", "").language("rust"))
                .documentation(DocumentationLink::cargo_book("reference/features.html"))
                .edit(Edit::replace(
                    &demo_file,
                    TextRange::new(0, original.len()),
                    original,
                    replacement,
                ))
                .command(
                    SuggestedCommand::new("cargo check")
                        .explanation("Verify the project after applying the edit."),
                ),
        );

    reporter.emit(&diagnostic)?;

    let fixer = Fixer::new().backups(true);

    match env::args().nth(1).as_deref() {
        Some("--check") => {
            let check = fixer.check(&diagnostic)?;

            println!();
            println!(
                "{} machine-applicable suggestion(s) validated.",
                check.applicable_suggestions
            );

            if check.affected_files.is_empty() {
                println!("No files would change.");
            } else {
                println!("Files that would change:");

                for file in check.affected_files {
                    println!("  {}", file.display());
                }
            }

            println!();
            println!("No files were modified.");
        }

        Some("--apply") => {
            let report = fixer.apply(&diagnostic)?;

            println!();
            println!("Applied {} suggestion(s).", report.applied_suggestions);

            if report.changed_files.is_empty() {
                println!("No files changed.");
            } else {
                for file in report.changed_files {
                    println!("Changed: {}", file.display());
                }
            }
        }

        Some("--interactive") => {
            fixer.apply_interactive(&diagnostic)?;
        }

        Some(argument) => {
            eprintln!("Unknown argument: {argument}");
            eprintln!();
            print_usage();
            std::process::exit(2);
        }

        None => {
            println!();
            println!("Preview only. No files were changed.");
            println!();
            print_usage();
        }
    }

    Ok(())
}

fn print_usage() {
    println!("Try:");
    println!("  cargo run --example intelligence");
    println!("  cargo run --example intelligence -- --check");
    println!("  cargo run --example intelligence -- --apply");
    println!(
        "  cargo run --features terminal-docs \
         --example intelligence -- --interactive"
    );
}