1use crate::error::FossilError;
8use std::fmt;
9
10pub type FsResult<T> = std::result::Result<T, FsError>;
12
13#[derive(Debug)]
15pub enum FsError {
16 PathNotFound(String),
18
19 NotFound(String),
21
22 NotADirectory(String),
24
25 NotAFile(String),
27
28 PathAlreadyExists(String),
30
31 PermissionDenied(String),
33
34 InvalidPath(String),
36
37 ReadOnlyRepository,
39
40 CannotModify { path: String, reason: String },
42
43 SymlinkError(String),
45
46 PatternError(String),
48
49 TransactionError(String),
51
52 Repository(FossilError),
54
55 Io(std::io::Error),
57
58 Encoding(String),
60
61 NotSupported(String),
63
64 FileTooLarge { path: String, size: u64, max: u64 },
66
67 LockTimeout,
69
70 CommitInProgress,
72
73 DatabaseError(String),
75
76 StagingError(String),
78
79 Other(String),
81}
82
83impl From<FossilError> for FsError {
84 fn from(err: FossilError) -> Self {
85 FsError::Repository(err)
86 }
87}
88
89impl From<std::io::Error> for FsError {
90 fn from(err: std::io::Error) -> Self {
91 FsError::Io(err)
92 }
93}
94
95impl From<std::string::FromUtf8Error> for FsError {
96 fn from(err: std::string::FromUtf8Error) -> Self {
97 FsError::Encoding(format!("UTF-8 conversion failed: {}", err))
98 }
99}
100
101impl fmt::Display for FsError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 FsError::PathNotFound(path) => write!(f, "Path not found: {}", path),
105 FsError::NotFound(path) => write!(f, "Not found: {}", path),
106 FsError::NotADirectory(path) => write!(f, "Not a directory: {}", path),
107 FsError::NotAFile(path) => write!(f, "Not a file: {}", path),
108 FsError::PathAlreadyExists(path) => write!(f, "Path already exists: {}", path),
109 FsError::PermissionDenied(msg) => write!(f, "Permission denied: {}", msg),
110 FsError::InvalidPath(path) => write!(f, "Invalid path: {}", path),
111 FsError::ReadOnlyRepository => {
112 write!(
113 f,
114 "Repository is read-only. Use Repository::open_rw() for write access."
115 )
116 }
117 FsError::CannotModify { path, reason } => {
118 write!(f, "Cannot modify {}: {}", path, reason)
119 }
120 FsError::SymlinkError(msg) => write!(f, "Symlink error: {}", msg),
121 FsError::PatternError(msg) => write!(f, "Pattern error: {}", msg),
122 FsError::TransactionError(msg) => write!(f, "Transaction error: {}", msg),
123 FsError::Repository(err) => write!(f, "Repository error: {}", err),
124 FsError::Io(err) => write!(f, "IO error: {}", err),
125 FsError::Encoding(msg) => write!(f, "Encoding error: {}", msg),
126 FsError::NotSupported(msg) => write!(f, "Not supported: {}", msg),
127 FsError::FileTooLarge { path, size, max } => {
128 write!(
129 f,
130 "File too large: {} ({} bytes, max {} bytes)",
131 path, size, max
132 )
133 }
134 FsError::LockTimeout => write!(f, "Lock acquisition timeout"),
135 FsError::CommitInProgress => write!(f, "Commit is currently in progress"),
136 FsError::DatabaseError(msg) => write!(f, "Database error: {}", msg),
137 FsError::StagingError(msg) => write!(f, "Staging error: {}", msg),
138 FsError::Other(msg) => write!(f, "{}", msg),
139 }
140 }
141}
142
143impl std::error::Error for FsError {}
144
145impl From<FsError> for FossilError {
147 fn from(err: FsError) -> Self {
148 match err {
149 FsError::Repository(fossil_err) => fossil_err,
150 other => FossilError::Io(std::io::Error::new(
151 std::io::ErrorKind::Other,
152 other.to_string(),
153 )),
154 }
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_error_display() {
164 let err = FsError::PathNotFound("/tmp/test.txt".to_string());
165 assert_eq!(err.to_string(), "Path not found: /tmp/test.txt");
166
167 let err = FsError::ReadOnlyRepository;
168 assert!(err.to_string().contains("read-only"));
169 }
170
171 #[test]
172 fn test_error_conversion() {
173 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "test");
174 let fs_err = FsError::from(io_err);
175 assert!(matches!(fs_err, FsError::Io(_)));
176 }
177}