use thiserror::Error;
#[derive(Error, Debug)]
pub enum LlmError {
#[error("Database not found: {path}")]
DatabaseNotFound { path: String },
#[error("Database corrupted: {reason}")]
DatabaseCorrupted { reason: String },
#[error("Schema mismatch: {reason}")]
SchemaMismatch { reason: String },
#[error("Invalid query: {query}")]
InvalidQuery { query: String },
#[error("Query cannot be empty")]
EmptyQuery,
#[error("Regex pattern rejected: {reason}")]
RegexRejected { reason: String },
#[error("Resource limit exceeded: {resource} (max: {limit}, provided: {provided})")]
ResourceLimitExceeded {
resource: String,
limit: usize,
provided: usize,
},
#[error("Path validation failed: {path} - {reason}")]
PathValidationFailed { path: String, reason: String },
#[error("Search failed: {reason}")]
SearchFailed { reason: String },
#[error("Invalid path: {path}")]
InvalidPath { path: String },
#[error("Invalid field: {field}")]
InvalidField { field: String },
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[error("SQLite error: {0}")]
SqliteError(#[from] rusqlite::Error),
#[error("JSON error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Regex error: {0}")]
RegexError(#[from] regex::Error),
#[error(
"LLM-E105: Magellan CLI not found in PATH.\n\n\
Magellan v2.1.0+ is required for algorithm features (--reachable-from, --dead-code-in, etc.).\n\n\
Installation methods:\n\
\x20 1. Cargo: cargo install magellan\n\
\x20 2. Binary: cargo binstall magellan\n\
\x20 3. Docs: https://docs.rs/magellan\n\n\
After installation, run: magellan --version"
)]
MagellanNotFound,
#[error(
"Ambiguous symbol name '{name}': {count} matches. Use --symbol-id with full SymbolId."
)]
AmbiguousSymbolName { name: String, count: usize },
#[error("Magellan version {current} is incompatible. Required: {required}")]
MagellanVersionMismatch { current: String, required: String },
#[error("Magellan {algorithm} execution failed: {stderr}")]
MagellanExecutionFailed { algorithm: String, stderr: String },
#[error("LLM-E110: Backend detection failed for database: {path}\nReason: {reason}")]
BackendDetectionFailed { path: String, reason: String },
#[error(
"LLM-E112: Symbol not found: {fqn}\n\n\
The fully-qualified name does not exist in the database.\n\n\
Suggestions:\n\
\x20 1. Use 'llmgrep --db {db} complete --partial \"{partial}\"' to find similar symbols\n\
\x20 2. Check the FQN spelling and module path\n\
\x20 3. Verify the database is up to date: magellan status --db {db}\n\n\
For more information, see: https://docs.rs/llmgrep/latest/llmgrep/"
)]
SymbolNotFound {
fqn: String,
db: String,
partial: String,
},
#[error(
"LLM-E113: Code chunks not available for {backend} backend.\n\n\
{message}\n\n\
Chunk retrieval requires Geometric (.geo) backend with chunking enabled.\n\n\
To use chunk retrieval:\n\
\x20 1. Reindex with chunking: magellan watch --root . --db code.geo --chunk\n\
\x20 2. Query using: llmgrep --db code.geo <query> --with-chunks\n\n\
For more information, see: https://docs.rs/llmgrep/latest/llmgrep/"
)]
ChunksNotAvailable { backend: String, message: String },
#[error(
"LLM-E114: The '{feature}' feature is not available for {backend} backend.\n\n\
{message}\n\n\
For more information, see: https://docs.rs/llmgrep/latest/llmgrep/"
)]
FeatureNotAvailable {
feature: String,
backend: String,
message: String,
},
}
impl LlmError {
pub const fn error_code(&self) -> &'static str {
match self {
LlmError::DatabaseNotFound { .. } => "LLM-E001",
LlmError::DatabaseCorrupted { .. } => "LLM-E002",
LlmError::SchemaMismatch { .. } => "LLM-E003",
LlmError::InvalidQuery { .. } => "LLM-E011",
LlmError::EmptyQuery => "LLM-E012",
LlmError::RegexRejected { .. } => "LLM-E101",
LlmError::ResourceLimitExceeded { .. } => "LLM-E102",
LlmError::PathValidationFailed { .. } => "LLM-E103",
LlmError::SearchFailed { .. } => "LLM-E021",
LlmError::InvalidPath { .. } => "LLM-E031",
LlmError::InvalidField { .. } => "LLM-E032",
LlmError::IoError(_) => "LLM-E901",
LlmError::SqliteError(_) => "LLM-E902",
LlmError::JsonError(_) => "LLM-E903",
LlmError::RegexError(_) => "LLM-E904",
LlmError::MagellanNotFound => "LLM-E105",
LlmError::AmbiguousSymbolName { .. } => "LLM-E106",
LlmError::MagellanVersionMismatch { .. } => "LLM-E107",
LlmError::MagellanExecutionFailed { .. } => "LLM-E108",
LlmError::BackendDetectionFailed { .. } => "LLM-E110",
LlmError::SymbolNotFound { .. } => "LLM-E112",
LlmError::ChunksNotAvailable { .. } => "LLM-E113",
LlmError::FeatureNotAvailable { .. } => "LLM-E114",
}
}
pub const fn severity(&self) -> &'static str {
match self {
LlmError::InvalidField { .. } => "warning",
LlmError::JsonError(_) | LlmError::RegexError(_) => "error",
LlmError::RegexRejected { .. } => "error",
LlmError::ResourceLimitExceeded { .. } => "error",
LlmError::PathValidationFailed { .. } => "error",
LlmError::MagellanNotFound => "error",
LlmError::AmbiguousSymbolName { .. } => "error",
LlmError::MagellanVersionMismatch { .. } => "error",
LlmError::MagellanExecutionFailed { .. } => "error",
LlmError::BackendDetectionFailed { .. } => "error",
LlmError::SymbolNotFound { .. } => "error",
LlmError::ChunksNotAvailable { .. } => "warning",
LlmError::FeatureNotAvailable { .. } => "error",
_ => "error",
}
}
pub const fn remediation(&self) -> Option<&'static str> {
match self {
LlmError::DatabaseNotFound { .. } => {
Some("Ensure the database path is correct and the file exists.")
}
LlmError::DatabaseCorrupted { .. } => {
Some("The database file may be corrupted. Try reindexing your codebase.")
}
LlmError::SchemaMismatch { .. } => {
Some("The database schema version is incompatible. Try reindexing with a compatible Magellan version or upgrade llmgrep.")
}
LlmError::InvalidQuery { .. } => {
Some("Check that your query is properly formatted and valid.")
}
LlmError::EmptyQuery => Some("Provide a non-empty query string using --query."),
LlmError::SearchFailed { .. } => {
Some("Check that the database is valid and the query is supported.")
}
LlmError::InvalidPath { .. } => Some("Ensure the path is valid and accessible."),
LlmError::InvalidField { .. } => {
Some("Valid fields: context, snippet, score, fqn, canonical_fqn, display_fqn, all")
}
LlmError::IoError(_) => Some("Check file permissions and disk space."),
LlmError::SqliteError(_) => {
Some("The database may be locked or corrupted. Try reopening the database.")
}
LlmError::JsonError(_) => {
Some("JSON serialization error. This may indicate corrupted data.")
}
LlmError::RegexError(_) => Some("Invalid regular expression. Check your query syntax."),
LlmError::RegexRejected { .. } => Some(
"Simplify the regex pattern or avoid nested quantifiers and excessive alternation",
),
LlmError::ResourceLimitExceeded { .. } => {
Some("Reduce the resource value to within the allowed maximum")
}
LlmError::PathValidationFailed { .. } => {
Some("Check that the path exists and is within the allowed directory structure")
}
LlmError::MagellanNotFound => {
Some("Install magellan v2.1.0+: cargo install magellan")
}
LlmError::AmbiguousSymbolName { .. } => {
Some("Use --symbol-id with full 32-character SymbolId for unambiguous reference.")
}
LlmError::MagellanVersionMismatch { .. } => {
Some("Update magellan: cargo install magellan-cli --force")
}
LlmError::MagellanExecutionFailed { .. } => {
Some("Check magellan --version and database compatibility.")
}
LlmError::BackendDetectionFailed { .. } => {
Some("Check that the database file exists and is a valid Magellan database.")
}
LlmError::SymbolNotFound { .. } => {
Some("Use 'complete' command with --partial flag to find similar symbols by partial name match.")
}
LlmError::ChunksNotAvailable { .. } => {
Some("Reindex with chunking enabled: magellan watch --root . --db code.geo --chunk")
}
LlmError::FeatureNotAvailable { .. } => {
Some("This feature is not supported by the current backend. Use a different backend or check available features.")
}
}
}
}