use thiserror::Error;
pub type Result<T> = std::result::Result<T, OpenCodeError>;
#[derive(Error, Debug)]
pub enum OpenCodeError {
#[error(
"OpenCode CLI not found: {0}. Install with: curl -fsSL https://opencode.ai/install | bash"
)]
CliNotFound(String),
#[error("CLI execution failed: {0}")]
CliExecution(String),
#[error("CLI exited with code {code}: {stderr}")]
CliExitError { code: i32, stderr: String },
#[error("CLI operation timed out after {0} seconds")]
Timeout(u64),
#[error("Failed to parse CLI output: {0}")]
OutputParse(String),
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Invalid configuration: {0}")]
InvalidConfig(String),
#[error("MCP server error: {0}")]
McpError(String),
#[error("OpenCode authentication required. Run: opencode auth login")]
AuthRequired,
#[error("No results found for: {0}")]
NoResults(String),
#[error("Rate limit exceeded. Try again later.")]
RateLimited,
#[error("Model not available: {0}. Check your subscription.")]
ModelUnavailable(String),
#[error("Failed to collect files: {0}")]
FileCollection(String),
}
impl OpenCodeError {
pub fn cli_error(message: impl Into<String>) -> Self {
Self::CliExecution(message.into())
}
pub fn exit_error(code: i32, stderr: impl Into<String>) -> Self {
Self::CliExitError {
code,
stderr: stderr.into(),
}
}
pub fn timeout(seconds: u64) -> Self {
Self::Timeout(seconds)
}
pub fn parse_error(message: impl Into<String>) -> Self {
Self::OutputParse(message.into())
}
pub fn is_retryable(&self) -> bool {
matches!(
self,
Self::Timeout(_) | Self::RateLimited | Self::CliExecution(_)
)
}
pub fn needs_auth(&self) -> bool {
matches!(self, Self::AuthRequired)
}
pub fn is_config_error(&self) -> bool {
matches!(
self,
Self::CliNotFound(_) | Self::InvalidConfig(_) | Self::ModelUnavailable(_)
)
}
}