use crate::error::FossilError;
use std::fmt;
pub type FsResult<T> = std::result::Result<T, FsError>;
#[derive(Debug)]
pub enum FsError {
PathNotFound(String),
NotFound(String),
NotADirectory(String),
NotAFile(String),
PathAlreadyExists(String),
PermissionDenied(String),
InvalidPath(String),
ReadOnlyRepository,
CannotModify { path: String, reason: String },
SymlinkError(String),
PatternError(String),
TransactionError(String),
Repository(FossilError),
Io(std::io::Error),
Encoding(String),
NotSupported(String),
FileTooLarge { path: String, size: u64, max: u64 },
LockTimeout,
CommitInProgress,
DatabaseError(String),
StagingError(String),
Other(String),
}
impl From<FossilError> for FsError {
fn from(err: FossilError) -> Self {
FsError::Repository(err)
}
}
impl From<std::io::Error> for FsError {
fn from(err: std::io::Error) -> Self {
FsError::Io(err)
}
}
impl From<std::string::FromUtf8Error> for FsError {
fn from(err: std::string::FromUtf8Error) -> Self {
FsError::Encoding(format!("UTF-8 conversion failed: {}", err))
}
}
impl fmt::Display for FsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FsError::PathNotFound(path) => write!(f, "Path not found: {}", path),
FsError::NotFound(path) => write!(f, "Not found: {}", path),
FsError::NotADirectory(path) => write!(f, "Not a directory: {}", path),
FsError::NotAFile(path) => write!(f, "Not a file: {}", path),
FsError::PathAlreadyExists(path) => write!(f, "Path already exists: {}", path),
FsError::PermissionDenied(msg) => write!(f, "Permission denied: {}", msg),
FsError::InvalidPath(path) => write!(f, "Invalid path: {}", path),
FsError::ReadOnlyRepository => {
write!(
f,
"Repository is read-only. Use Repository::open_rw() for write access."
)
}
FsError::CannotModify { path, reason } => {
write!(f, "Cannot modify {}: {}", path, reason)
}
FsError::SymlinkError(msg) => write!(f, "Symlink error: {}", msg),
FsError::PatternError(msg) => write!(f, "Pattern error: {}", msg),
FsError::TransactionError(msg) => write!(f, "Transaction error: {}", msg),
FsError::Repository(err) => write!(f, "Repository error: {}", err),
FsError::Io(err) => write!(f, "IO error: {}", err),
FsError::Encoding(msg) => write!(f, "Encoding error: {}", msg),
FsError::NotSupported(msg) => write!(f, "Not supported: {}", msg),
FsError::FileTooLarge { path, size, max } => {
write!(
f,
"File too large: {} ({} bytes, max {} bytes)",
path, size, max
)
}
FsError::LockTimeout => write!(f, "Lock acquisition timeout"),
FsError::CommitInProgress => write!(f, "Commit is currently in progress"),
FsError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
FsError::StagingError(msg) => write!(f, "Staging error: {}", msg),
FsError::Other(msg) => write!(f, "{}", msg),
}
}
}
impl std::error::Error for FsError {}
impl From<FsError> for FossilError {
fn from(err: FsError) -> Self {
match err {
FsError::Repository(fossil_err) => fossil_err,
other => FossilError::Io(std::io::Error::new(
std::io::ErrorKind::Other,
other.to_string(),
)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = FsError::PathNotFound("/tmp/test.txt".to_string());
assert_eq!(err.to_string(), "Path not found: /tmp/test.txt");
let err = FsError::ReadOnlyRepository;
assert!(err.to_string().contains("read-only"));
}
#[test]
fn test_error_conversion() {
let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "test");
let fs_err = FsError::from(io_err);
assert!(matches!(fs_err, FsError::Io(_)));
}
}