use std::path::PathBuf;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum GrepError {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Invalid path specifier '{path}': {reason}")]
InvalidPath {
path: String,
reason: String,
},
#[error("Unsupported compression format: {0}")]
UnsupportedCompression(String),
#[error("Unsupported archive format: {0}")]
UnsupportedArchive(String),
#[error("Archive entry not found: {entry} in {archive}")]
EntryNotFound {
archive: PathBuf,
entry: String,
},
#[error("No entries in {archive} match filter '{filter}'")]
NoMatchingEntries {
archive: PathBuf,
filter: String,
},
#[error("Invalid UTF-8 in {file_path}: {message}")]
InvalidUtf8 {
file_path: String,
message: String,
},
#[error("Decompression error for {file_path}: {message}")]
Decompression {
file_path: PathBuf,
message: String,
},
#[error("Archive error for {file_path}: {message}")]
Archive {
file_path: PathBuf,
message: String,
},
#[error("Invalid glob pattern '{pattern}': {message}")]
GlobPattern {
pattern: String,
message: String,
},
#[error("Archive {archive} is password-protected")]
PasswordProtected {
archive: PathBuf,
},
#[error("Nested archives are not supported: {inner} inside {outer}")]
NestedArchive {
outer: PathBuf,
inner: String,
},
#[error("Binary file detected: {0}")]
BinaryFile(PathBuf),
#[error("Feature '{feature}' is required but not enabled. Rebuild with --features {feature}")]
FeatureNotEnabled {
feature: String,
},
#[error("Failed to extract text from {file_path}: {message}")]
DocumentExtraction {
file_path: PathBuf,
message: String,
},
#[error("Document contains no extractable text: {0}")]
DocumentEmpty(PathBuf),
#[error("Unsupported document format: {0}")]
UnsupportedDocument(String),
#[error("OCR failed for {file_path}: {message}")]
OcrError {
file_path: PathBuf,
message: String,
},
#[error("OCR not available: {0}")]
OcrNotAvailable(String),
}
pub type GrepResult<T> = Result<T, GrepError>;
impl GrepError {
pub fn invalid_path(path: impl Into<String>, reason: impl Into<String>) -> Self {
Self::InvalidPath {
path: path.into(),
reason: reason.into(),
}
}
pub fn decompression(file_path: impl Into<PathBuf>, message: impl Into<String>) -> Self {
Self::Decompression {
file_path: file_path.into(),
message: message.into(),
}
}
pub fn archive(file_path: impl Into<PathBuf>, message: impl Into<String>) -> Self {
Self::Archive {
file_path: file_path.into(),
message: message.into(),
}
}
pub fn invalid_utf8(file_path: impl Into<String>, err: std::str::Utf8Error) -> Self {
Self::InvalidUtf8 {
file_path: file_path.into(),
message: err.to_string(),
}
}
pub fn glob_pattern(pattern: impl Into<String>, message: impl Into<String>) -> Self {
Self::GlobPattern {
pattern: pattern.into(),
message: message.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = GrepError::invalid_path("foo:bar:baz", "too many colons");
assert!(err.to_string().contains("foo:bar:baz"));
assert!(err.to_string().contains("too many colons"));
}
#[test]
fn test_io_error_conversion() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
let grep_err: GrepError = io_err.into();
assert!(matches!(grep_err, GrepError::Io(_)));
}
}