gatekpr-opencode 0.2.3

OpenCode CLI integration for RAG-powered validation enrichment
Documentation
//! Error types for OpenCode CLI integration

use thiserror::Error;

/// Result type for OpenCode operations
pub type Result<T> = std::result::Result<T, OpenCodeError>;

/// Errors that can occur when using OpenCode CLI
#[derive(Error, Debug)]
pub enum OpenCodeError {
    /// OpenCode CLI binary not found
    #[error(
        "OpenCode CLI not found: {0}. Install with: curl -fsSL https://opencode.ai/install | bash"
    )]
    CliNotFound(String),

    /// CLI execution failed
    #[error("CLI execution failed: {0}")]
    CliExecution(String),

    /// CLI returned non-zero exit code
    #[error("CLI exited with code {code}: {stderr}")]
    CliExitError { code: i32, stderr: String },

    /// CLI timed out
    #[error("CLI operation timed out after {0} seconds")]
    Timeout(u64),

    /// Failed to parse CLI output
    #[error("Failed to parse CLI output: {0}")]
    OutputParse(String),

    /// Failed to serialize/deserialize JSON
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),

    /// IO error during file operations
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

    /// Invalid configuration
    #[error("Invalid configuration: {0}")]
    InvalidConfig(String),

    /// MCP server error
    #[error("MCP server error: {0}")]
    McpError(String),

    /// Authentication required
    #[error("OpenCode authentication required. Run: opencode auth login")]
    AuthRequired,

    /// No results found
    #[error("No results found for: {0}")]
    NoResults(String),

    /// Rate limit exceeded
    #[error("Rate limit exceeded. Try again later.")]
    RateLimited,

    /// Model not available
    #[error("Model not available: {0}. Check your subscription.")]
    ModelUnavailable(String),

    /// File collection error
    #[error("Failed to collect files: {0}")]
    FileCollection(String),
}

impl OpenCodeError {
    /// Create a CLI execution error
    pub fn cli_error(message: impl Into<String>) -> Self {
        Self::CliExecution(message.into())
    }

    /// Create a CLI exit error
    pub fn exit_error(code: i32, stderr: impl Into<String>) -> Self {
        Self::CliExitError {
            code,
            stderr: stderr.into(),
        }
    }

    /// Create a timeout error
    pub fn timeout(seconds: u64) -> Self {
        Self::Timeout(seconds)
    }

    /// Create a parse error
    pub fn parse_error(message: impl Into<String>) -> Self {
        Self::OutputParse(message.into())
    }

    /// Check if error is retryable
    pub fn is_retryable(&self) -> bool {
        matches!(
            self,
            Self::Timeout(_) | Self::RateLimited | Self::CliExecution(_)
        )
    }

    /// Check if error requires authentication
    pub fn needs_auth(&self) -> bool {
        matches!(self, Self::AuthRequired)
    }

    /// Check if this is a configuration error
    pub fn is_config_error(&self) -> bool {
        matches!(
            self,
            Self::CliNotFound(_) | Self::InvalidConfig(_) | Self::ModelUnavailable(_)
        )
    }
}