cargo-docs-md 0.2.4

Generate per-module markdown documentation from rustdoc JSON output
Documentation
//! Error types for docs-md.
//!
//! This module defines all errors that can occur during documentation generation.
//! Errors use the `miette` crate for enhanced diagnostics, providing:
//! - Human-readable error messages
//! - Diagnostic codes for programmatic handling
//! - Helpful suggestions for resolution
//!
//! # Error Categories
//!
//! - **I/O Errors** (`FileRead`, `CreateDir`, `FileWrite`): File system operations
//! - **Parse Errors** (`JsonParse`): Invalid or malformed rustdoc JSON
//! - **Lookup Errors** (`ItemNotFound`): Missing items in the documentation index

use std::io as StdIO;
use std::path::PathBuf;

use miette::Diagnostic;
use serde_json as SJSON;
use thiserror::Error as ThisError;

/// Errors that can occur during documentation generation.
///
/// Each variant includes:
/// - A human-readable error message
/// - A diagnostic code (e.g., `docs_md::io::read`)
/// - Optional help text for resolution
/// - The underlying source error (where applicable)
#[derive(Debug, Diagnostic, ThisError)]
pub enum Error {
    /// Failed to read a file from disk.
    ///
    /// This typically occurs when:
    /// - The file doesn't exist
    /// - The process lacks read permissions
    /// - The path is invalid
    #[error("Failed to read file")]
    #[diagnostic(
        code(docs_md::io::read),
        help("Check that the file exists and is readable")
    )]
    FileRead(#[source] StdIO::Error),

    /// Failed to parse the rustdoc JSON file.
    ///
    /// This can happen when:
    /// - The file is not valid JSON
    /// - The JSON schema doesn't match `rustdoc-types` expectations
    /// - The file is from an incompatible rustdoc version
    #[error("Failed to parse rustdoc JSON")]
    #[diagnostic(
        code(docs_md::parse::json),
        help("Ensure the file is valid rustdoc JSON output (generated with --output-format json)")
    )]
    JsonParse(#[source] SJSON::Error),

    /// Failed to parse the rustdoc JSON file using SIMD-accelerated parser.
    ///
    /// This variant is used when the `simd-json` feature is enabled.
    #[cfg(feature = "simd-json")]
    #[error("Failed to parse rustdoc JSON (simd-json)")]
    #[diagnostic(
        code(docs_md::parse::simd_json),
        help("Ensure the file is valid rustdoc JSON output (generated with --output-format json)")
    )]
    SimdJsonParse(#[source] simd_json::Error),

    /// Failed to create the output directory.
    ///
    /// This typically occurs when:
    /// - The parent directory doesn't exist
    /// - The process lacks write permissions
    /// - The path is invalid or too long
    #[error("Failed to create output directory")]
    #[diagnostic(code(docs_md::io::mkdir))]
    CreateDir(#[source] StdIO::Error),

    /// Failed to write a markdown file.
    ///
    /// This typically occurs when:
    /// - The output directory is not writable
    /// - The disk is full
    /// - The file is locked by another process
    #[error("Failed to write file")]
    #[diagnostic(code(docs_md::io::write))]
    FileWrite(#[source] StdIO::Error),

    /// An item ID was not found in the crate's index.
    ///
    /// The rustdoc JSON index should contain all referenced items.
    /// This error indicates data inconsistency, possibly from:
    /// - Corrupted JSON
    /// - Incompatible rustdoc-types version
    /// - Items removed during filtering
    ///
    /// The string contains the ID that was not found.
    #[error("Item not found in index: {0}")]
    #[diagnostic(code(docs_md::parse::item_not_found))]
    ItemNotFound(String),

    // ========== Multi-crate errors ==========
    /// The specified directory path is invalid or inaccessible.
    ///
    /// This typically occurs when:
    /// - The path doesn't exist
    /// - The path is a file, not a directory
    /// - The process lacks read permissions
    #[error("Invalid directory: {0}")]
    #[diagnostic(
        code(docs_md::io::dir),
        help("Check that the directory exists and is readable")
    )]
    InvalidDirectory(String),

    /// No rustdoc JSON files were found in the specified directory.
    ///
    /// The directory should contain `.json` files generated by rustdoc
    /// with the `--output-format json` flag.
    #[error("No rustdoc JSON files found in directory: {0}")]
    #[diagnostic(
        code(docs_md::parse::no_json),
        help(
            "Run `RUSTDOCFLAGS='-Z unstable-options --output-format json' cargo +nightly doc` to generate JSON files"
        )
    )]
    NoJsonFiles(PathBuf),

    /// Multiple crates with the same name were found.
    ///
    /// Each JSON file should represent a unique crate. This error
    /// indicates duplicate crate names in the input directory.
    #[error("Duplicate crate name: {0}")]
    #[diagnostic(
        code(docs_md::parse::duplicate_crate),
        help("Remove duplicate JSON files or rename crates")
    )]
    DuplicateCrate(String),

    /// Could not determine the crate name from a JSON file.
    ///
    /// The rustdoc JSON format should include a root item with
    /// the crate name. This error indicates a malformed file.
    #[error("Could not determine crate name from JSON file: {0}")]
    #[diagnostic(
        code(docs_md::parse::no_crate_name),
        help("Ensure the file is valid rustdoc JSON with a root module item")
    )]
    NoCrateName(PathBuf),

    /// Failed to create a progress bar with the given template.
    ///
    /// This indicates an invalid progress bar template string.
    /// Since templates are compile-time constants, this error
    /// typically indicates a programming error.
    #[error("Invalid progress bar template")]
    #[diagnostic(
        code(docs_md::ui::progress_template),
        help("Check the progress bar template syntax")
    )]
    ProgressBarTemplate(#[source] indicatif::style::TemplateError),

    // ========== Source parsing errors (source-parsing feature) ==========
    /// Failed to locate crate source in the Cargo registry.
    ///
    /// This occurs when:
    /// - The crate is not downloaded in ~/.cargo/registry/src/
    /// - The specified version doesn't exist
    /// - The registry path is misconfigured
    #[cfg(feature = "source-parsing")]
    #[error("Source locator error: {0}")]
    #[diagnostic(
        code(docs_md::source::locator),
        help("Ensure the crate is a dependency and has been downloaded (cargo fetch)")
    )]
    SourceLocator(String),

    /// Failed to parse Rust source code with syn.
    ///
    /// This occurs when:
    /// - The source file contains invalid Rust syntax
    /// - The file cannot be read
    /// - Module resolution fails
    #[cfg(feature = "source-parsing")]
    #[error("Source parser error: {0}")]
    #[diagnostic(
        code(docs_md::source::parser),
        help("Check that the source files contain valid Rust code")
    )]
    SourceParser(String),

    /// Failed to collect dependency sources.
    ///
    /// This occurs when:
    /// - Cargo metadata cannot be loaded
    /// - The registry path doesn't exist
    /// - File copy operations fail
    /// - Too many .source_* directories exist
    #[cfg(feature = "source-parsing")]
    #[error("Source collector error: {0}")]
    #[diagnostic(
        code(docs_md::source::collector),
        help("Ensure cargo metadata is available and ~/.cargo/registry/src/ exists")
    )]
    SourceCollector(String),
}