1use crate::storage::bookmarks::BookmarkRootGuid;
6use crate::types::BookmarkType;
7use error_support::{ErrorHandling, GetErrorHandling};
8use interrupt_support::Interrupted;
9
10pub use error_support::{debug, error, info, trace, warn};
12
13pub type Result<T> = std::result::Result<T, Error>;
15pub type ApiResult<T> = std::result::Result<T, PlacesApiError>;
17
18#[derive(Debug, thiserror::Error)]
20pub enum PlacesApiError {
21 #[error("Unexpected error: {reason}")]
22 UnexpectedPlacesException { reason: String },
23
24 #[error("UrlParseFailed: {reason}")]
29 UrlParseFailed { reason: String },
30
31 #[error("PlacesConnectionBusy error: {reason}")]
32 PlacesConnectionBusy { reason: String },
33
34 #[error("Operation Interrupted: {reason}")]
35 OperationInterrupted { reason: String },
36
37 #[error("Unknown bookmark: {reason}")]
40 UnknownBookmarkItem { reason: String },
41
42 #[error("Invalid bookmark operation: {reason}")]
49 InvalidBookmarkOperation { reason: String },
50}
51
52#[derive(Debug, thiserror::Error)]
54pub enum Error {
55 #[error("Invalid place info: {0}")]
56 InvalidPlaceInfo(#[from] InvalidPlaceInfo),
57
58 #[error("The store is corrupt: {0}")]
59 Corruption(#[from] Corruption),
60
61 #[error("Error synchronizing: {0}")]
62 SyncAdapterError(#[from] sync15::Error),
63
64 #[error("Error merging: {0}")]
65 MergeError(#[from] dogear::Error),
66
67 #[error("Error parsing JSON data: {0}")]
68 JsonError(#[from] serde_json::Error),
69
70 #[error("Error executing SQL: {0}")]
71 SqlError(#[from] rusqlite::Error),
72
73 #[error("Error parsing URL: {0}")]
74 UrlParseError(#[from] url::ParseError),
75
76 #[error("A connection of this type is already open")]
77 ConnectionAlreadyOpen,
78
79 #[error("An invalid connection type was specified")]
80 InvalidConnectionType,
81
82 #[error("IO error: {0}")]
83 IoError(#[from] std::io::Error),
84
85 #[error("Operation interrupted")]
86 InterruptedError(#[from] Interrupted),
87
88 #[error("Tried to close connection on wrong PlacesApi instance")]
89 WrongApiForClose,
90
91 #[error("Incoming bookmark missing type")]
92 MissingBookmarkKind,
93
94 #[error("Synced bookmark has unsupported kind {0}")]
95 UnsupportedSyncedBookmarkKind(u8),
96
97 #[error("Synced bookmark has unsupported validity {0}")]
98 UnsupportedSyncedBookmarkValidity(u8),
99
100 #[error("Illegal database path: {0:?}")]
104 IllegalDatabasePath(std::path::PathBuf),
105
106 #[error("UTF8 Error: {0}")]
107 Utf8Error(#[from] std::str::Utf8Error),
108
109 #[error("Can not import from database version {0}")]
112 UnsupportedDatabaseVersion(i64),
113
114 #[error("Error opening database: {0}")]
115 OpenDatabaseError(#[from] sql_support::open_database::Error),
116
117 #[error("Invalid metadata observation: {0}")]
118 InvalidMetadataObservation(#[from] InvalidMetadataObservation),
119}
120
121#[derive(Debug, thiserror::Error)]
122pub enum InvalidPlaceInfo {
123 #[error("No url specified")]
124 NoUrl,
125 #[error("Invalid guid")]
126 InvalidGuid,
127 #[error("Invalid parent: {0}")]
128 InvalidParent(String),
129 #[error("Invalid child guid")]
130 InvalidChildGuid,
131
132 #[error("No such item: {0}")]
135 NoSuchGuid(String),
136
137 #[error("No such url")]
140 NoSuchUrl,
141
142 #[error("Can't update a bookmark of type {0} with one of type {1}")]
143 MismatchedBookmarkType(u8, u8),
144
145 #[error("URL too long")]
148 UrlTooLong,
149
150 #[error("The tag value is invalid")]
152 InvalidTag,
153 #[error("Cannot change the '{0}' property of a bookmark of type {1:?}")]
154 IllegalChange(&'static str, BookmarkType),
155
156 #[error("Cannot update the bookmark root {0:?}")]
157 CannotUpdateRoot(BookmarkRootGuid),
158}
159
160#[derive(Debug, thiserror::Error)]
165pub enum Corruption {
166 #[error("Bookmark '{0}' has a parent of '{1}' which does not exist")]
167 NoParent(String, String),
168
169 #[error("The local roots are invalid")]
170 InvalidLocalRoots,
171
172 #[error("The synced roots are invalid")]
173 InvalidSyncedRoots,
174
175 #[error("Bookmark '{0}' has no parent but is not the bookmarks root")]
176 NonRootWithoutParent(String),
177}
178
179#[derive(Debug, thiserror::Error)]
180pub enum InvalidMetadataObservation {
181 #[error("Observed view time is invalid (too long)")]
182 ViewTimeTooLong,
183}
184
185impl GetErrorHandling for Error {
188 type ExternalError = PlacesApiError;
189
190 fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
191 match self {
192 Error::InvalidPlaceInfo(info) => {
193 let label = info.to_string();
194 ErrorHandling::convert(match &info {
195 InvalidPlaceInfo::InvalidParent(..) => {
196 PlacesApiError::InvalidBookmarkOperation { reason: label }
197 }
198 InvalidPlaceInfo::UrlTooLong => {
199 PlacesApiError::UrlParseFailed { reason: label }
200 }
201 InvalidPlaceInfo::NoSuchGuid(..) => {
202 PlacesApiError::UnknownBookmarkItem { reason: label }
203 }
204 InvalidPlaceInfo::IllegalChange(..) => {
205 PlacesApiError::InvalidBookmarkOperation { reason: label }
206 }
207 InvalidPlaceInfo::CannotUpdateRoot(..) => {
208 PlacesApiError::InvalidBookmarkOperation { reason: label }
209 }
210 _ => PlacesApiError::UnexpectedPlacesException { reason: label },
211 })
212 .report_error("places-invalid-place-info")
213 }
214 Error::UrlParseError(e) => {
215 ErrorHandling::convert(PlacesApiError::UrlParseFailed {
218 reason: e.to_string(),
219 })
220 .log_warning()
221 }
222 Error::SqlError(rusqlite::Error::SqliteFailure(err, _)) => match err.code {
223 rusqlite::ErrorCode::DatabaseBusy => {
224 ErrorHandling::convert(PlacesApiError::PlacesConnectionBusy {
225 reason: self.to_string(),
226 })
227 .log_warning()
228 }
229 rusqlite::ErrorCode::OperationInterrupted => {
230 ErrorHandling::convert(PlacesApiError::OperationInterrupted {
231 reason: self.to_string(),
232 })
233 .log_info()
234 }
235 rusqlite::ErrorCode::DatabaseCorrupt => {
236 ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
237 reason: self.to_string(),
238 })
239 .report_error("places-db-corrupt")
240 }
241 rusqlite::ErrorCode::DiskFull => {
242 ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
243 reason: self.to_string(),
244 })
245 .report_error("places-db-disk-full")
246 }
247 _ => ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
248 reason: self.to_string(),
249 })
250 .report_error("places-unexpected"),
251 },
252 Error::InterruptedError(err) => {
253 ErrorHandling::convert(PlacesApiError::OperationInterrupted {
255 reason: err.to_string(),
256 })
257 .log_info()
258 }
259 Error::Corruption(e) => {
260 ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
261 reason: e.to_string(),
262 })
263 .report_error("places-bookmarks-corruption")
264 }
265 Error::SyncAdapterError(e) => {
266 match e {
267 sync15::Error::StoreError(store_error) => {
268 if let Some(places_err) = store_error.downcast_ref::<Error>() {
271 info!("Recursing to resolve places error");
272 places_err.get_error_handling()
273 } else {
274 ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
275 reason: self.to_string(),
276 })
277 .report_error("places-unexpected-sync-error")
278 }
279 }
280 _ => ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
281 reason: self.to_string(),
282 })
283 .report_error("places-unexpected-sync-error"),
284 }
285 }
286 Error::InvalidMetadataObservation(InvalidMetadataObservation::ViewTimeTooLong) => {
287 ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
288 reason: self.to_string(),
289 })
290 .log_warning()
291 }
292 _ => ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
293 reason: self.to_string(),
294 })
295 .report_error("places-unexpected-error"),
296 }
297 }
298}