1use coven_database::{DbError, LocalFileError};
4use coven_foundation::{
5 files::{FileError, StoreLockError},
6 id_source::{DeviceId, FileId},
7};
8use coven_storage::StorageError;
9use std::path::PathBuf;
10
11#[derive(Debug, thiserror::Error)]
13pub enum FileReadError {
14 #[error("an update is required to read this file")]
16 UpdateRequired(crate::RequiredUpdate),
17 #[error("no storage connected")]
19 NoStorage,
20 #[error("file {id}, uploaded by device {device:?}, is missing from storage")]
23 Missing {
24 id: String,
26 device: DeviceId,
28 },
29 #[error("file {file} is only on device {device:?}")]
33 DeviceOnly {
34 file: FileId,
36 device: DeviceId,
38 },
39 #[error("file {id} is missing at {}", path.display())]
41 UserFileMissing {
42 id: String,
44 path: PathBuf,
46 },
47 #[error("file {id} changed at {}", path.display())]
49 UserFileChanged {
50 id: String,
52 path: PathBuf,
54 },
55 #[error("file {id} failed its content check")]
57 Integrity {
58 id: String,
60 #[source]
63 cause: Option<std::sync::Arc<coven_crypto::CryptoError>>,
64 },
65 #[error("file {id} range {offset}..{end} exceeds {size}")]
67 RangeOutOfBounds {
68 id: String,
70 offset: u64,
72 end: u64,
74 size: u64,
76 },
77 #[error(transparent)]
79 Database(DbError),
80 #[error(transparent)]
82 Disk(FileError),
83 #[error(transparent)]
85 Lock(StoreLockError),
86 #[error(transparent)]
88 Storage(StorageError),
89 #[error(transparent)]
93 Sync(Box<crate::SyncError>),
94 #[error(transparent)]
96 Bug(#[from] coven_foundation::task::Bug),
97}
98impl From<FileError> for FileReadError {
99 fn from(error: FileError) -> Self {
100 match error {
101 FileError::Bug(bug) => Self::Bug(bug),
102 error => Self::Disk(error),
103 }
104 }
105}
106impl From<StoreLockError> for FileReadError {
107 fn from(error: StoreLockError) -> Self {
108 match error {
109 StoreLockError::Bug(bug) => Self::Bug(bug),
110 error => Self::Lock(error),
111 }
112 }
113}
114impl From<StorageError> for FileReadError {
115 fn from(error: StorageError) -> Self {
116 match error {
117 StorageError::Bug(bug) => Self::Bug(bug),
118 error => Self::Storage(error),
119 }
120 }
121}
122impl coven_foundation::task::CarriesBug for FileReadError {
123 fn bug(&self) -> Option<&coven_foundation::task::Bug> {
124 match self {
125 Self::Bug(bug) => Some(bug),
126 Self::Database(error) => error.bug(),
127 Self::Disk(error) => error.bug(),
128 Self::Lock(error) => error.bug(),
129 Self::Storage(error) => error.bug(),
130 Self::Sync(error) => error.bug(),
131 _ => None,
132 }
133 }
134}
135impl From<coven_foundation::files::WriterError> for FileReadError {
136 fn from(error: coven_foundation::files::WriterError) -> Self {
137 match error {
138 coven_foundation::files::WriterError::Lock(error) => error.into(),
139 coven_foundation::files::WriterError::File(error) => error.into(),
140 }
141 }
142}
143
144impl From<crate::SyncError> for FileReadError {
147 fn from(error: crate::SyncError) -> Self {
148 match error {
149 crate::SyncError::Database(error) => error.into(),
150 crate::SyncError::Storage(error) => error.into(),
151 crate::SyncError::Bug(bug) => Self::Bug(bug),
152 error => Self::Sync(Box::new(error)),
153 }
154 }
155}
156impl From<DbError> for FileReadError {
157 fn from(error: DbError) -> Self {
158 match error {
159 DbError::Disk(error) => Self::Disk(error),
160 DbError::Lock(error) => Self::Lock(error),
161 DbError::Bug(bug) => Self::Bug(bug),
162 error => Self::Database(error),
163 }
164 }
165}
166impl FileReadError {
167 pub(crate) fn chunk(id: String, error: coven_format::file::FileError) -> Self {
171 Self::Integrity {
172 id,
173 cause: match error {
174 coven_format::file::FileError::Crypto(error) => Some(std::sync::Arc::new(error)),
175 coven_format::file::FileError::Format(_) => None,
176 },
177 }
178 }
179}
180impl From<LocalFileError> for FileReadError {
181 fn from(error: LocalFileError) -> Self {
182 match error {
183 LocalFileError::UserFileMissing { id, path } => Self::UserFileMissing { id, path },
184 LocalFileError::UserFileChanged { id, path } => Self::UserFileChanged { id, path },
185 LocalFileError::Integrity { id } => Self::Integrity { id, cause: None },
186 LocalFileError::RangeOutOfBounds { id, offset, end, size } => {
187 Self::RangeOutOfBounds { id, offset, end, size }
188 }
189 LocalFileError::Database(error) => error.into(),
190 LocalFileError::Disk(error) => Self::Disk(error),
191 LocalFileError::Lock(error) => Self::Lock(error),
192 LocalFileError::Bug(bug) => Self::Bug(bug),
193 }
194 }
195}
196
197#[derive(Debug, thiserror::Error)]
200pub(crate) enum TransferError {
201 #[error("file transfer was refused: {0:?}")]
203 Refused(crate::Refusal),
204 #[error(transparent)]
206 File(#[from] FileReadError),
207}
208
209impl coven_foundation::task::CarriesBug for TransferError {
210 fn bug(&self) -> Option<&coven_foundation::task::Bug> {
211 match self {
212 Self::Refused(_) => None,
213 Self::File(error) => error.bug(),
214 }
215 }
216}
217
218impl From<StorageError> for TransferError {
219 fn from(error: StorageError) -> Self {
220 Self::File(error.into())
221 }
222}
223impl From<DbError> for TransferError {
224 fn from(error: DbError) -> Self {
225 Self::File(error.into())
226 }
227}
228impl From<FileError> for TransferError {
229 fn from(error: FileError) -> Self {
230 Self::File(error.into())
231 }
232}
233impl From<LocalFileError> for TransferError {
234 fn from(error: LocalFileError) -> Self {
235 Self::File(error.into())
236 }
237}
238
239#[cfg(test)]
240#[path = "file_error_tests.rs"]
241mod tests;