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 merging: {0}")]
62 MergeError(#[from] dogear::Error),
63
64 #[error("Error parsing JSON data: {0}")]
65 JsonError(#[from] serde_json::Error),
66
67 #[error("Error executing SQL: {0}")]
68 SqlError(#[from] rusqlite::Error),
69
70 #[error("Error parsing URL: {0}")]
71 UrlParseError(#[from] url::ParseError),
72
73 #[error("A connection of this type is already open")]
74 ConnectionAlreadyOpen,
75
76 #[error("An invalid connection type was specified")]
77 InvalidConnectionType,
78
79 #[error("IO error: {0}")]
80 IoError(#[from] std::io::Error),
81
82 #[error("Operation interrupted")]
83 InterruptedError(#[from] Interrupted),
84
85 #[error("Tried to close connection on wrong PlacesApi instance")]
86 WrongApiForClose,
87
88 #[error("Incoming bookmark missing type")]
89 MissingBookmarkKind,
90
91 #[error("Synced bookmark has unsupported kind {0}")]
92 UnsupportedSyncedBookmarkKind(u8),
93
94 #[error("Synced bookmark has unsupported validity {0}")]
95 UnsupportedSyncedBookmarkValidity(u8),
96
97 #[error("Illegal database path: {0:?}")]
101 IllegalDatabasePath(std::path::PathBuf),
102
103 #[error("UTF8 Error: {0}")]
104 Utf8Error(#[from] std::str::Utf8Error),
105
106 #[error("Can not import from database version {0}")]
109 UnsupportedDatabaseVersion(i64),
110
111 #[error("Error opening database: {0}")]
112 OpenDatabaseError(#[from] sql_support::open_database::Error),
113
114 #[error("Invalid metadata observation: {0}")]
115 InvalidMetadataObservation(#[from] InvalidMetadataObservation),
116}
117
118impl From<sql_support::path::Error> for Error {
119 fn from(e: sql_support::path::Error) -> Self {
120 match e {
121 sql_support::path::Error::IllegalDatabasePath(path) => Error::IllegalDatabasePath(path),
122 sql_support::path::Error::IoError(e) => Error::IoError(e),
123 }
124 }
125}
126
127#[derive(Debug, thiserror::Error)]
128pub enum InvalidPlaceInfo {
129 #[error("No url specified")]
130 NoUrl,
131 #[error("Invalid guid")]
132 InvalidGuid,
133 #[error("Invalid parent: {0}")]
134 InvalidParent(String),
135 #[error("Invalid child guid")]
136 InvalidChildGuid,
137
138 #[error("No such item: {0}")]
141 NoSuchGuid(String),
142
143 #[error("No such url")]
146 NoSuchUrl,
147
148 #[error("Can't update a bookmark of type {0} with one of type {1}")]
149 MismatchedBookmarkType(u8, u8),
150
151 #[error("URL too long")]
154 UrlTooLong,
155
156 #[error("The tag value is invalid")]
158 InvalidTag,
159 #[error("Cannot change the '{0}' property of a bookmark of type {1:?}")]
160 IllegalChange(&'static str, BookmarkType),
161
162 #[error("Cannot update the bookmark root {0:?}")]
163 CannotUpdateRoot(BookmarkRootGuid),
164}
165
166#[derive(Debug, thiserror::Error)]
171pub enum Corruption {
172 #[error("Bookmark '{0}' has a parent of '{1}' which does not exist")]
173 NoParent(String, String),
174
175 #[error("The local roots are invalid")]
176 InvalidLocalRoots,
177
178 #[error("The synced roots are invalid")]
179 InvalidSyncedRoots,
180
181 #[error("Bookmark '{0}' has no parent but is not the bookmarks root")]
182 NonRootWithoutParent(String),
183}
184
185#[derive(Debug, thiserror::Error)]
186pub enum InvalidMetadataObservation {
187 #[error("Observed view time is invalid (too long)")]
188 ViewTimeTooLong,
189}
190
191impl GetErrorHandling for Error {
194 type ExternalError = PlacesApiError;
195
196 fn get_error_handling(&self) -> ErrorHandling<Self::ExternalError> {
197 match self {
198 Error::InvalidPlaceInfo(info) => {
199 let label = info.to_string();
200 ErrorHandling::convert(match &info {
201 InvalidPlaceInfo::InvalidParent(..) => {
202 PlacesApiError::InvalidBookmarkOperation { reason: label }
203 }
204 InvalidPlaceInfo::UrlTooLong => {
205 PlacesApiError::UrlParseFailed { reason: label }
206 }
207 InvalidPlaceInfo::NoSuchGuid(..) => {
208 PlacesApiError::UnknownBookmarkItem { reason: label }
209 }
210 InvalidPlaceInfo::IllegalChange(..) => {
211 PlacesApiError::InvalidBookmarkOperation { reason: label }
212 }
213 InvalidPlaceInfo::CannotUpdateRoot(..) => {
214 PlacesApiError::InvalidBookmarkOperation { reason: label }
215 }
216 _ => PlacesApiError::UnexpectedPlacesException { reason: label },
217 })
218 .report_error("places-invalid-place-info")
219 }
220 Error::UrlParseError(e) => {
221 ErrorHandling::convert(PlacesApiError::UrlParseFailed {
224 reason: e.to_string(),
225 })
226 .log_warning()
227 }
228 Error::SqlError(rusqlite::Error::SqliteFailure(err, _)) => match err.code {
229 rusqlite::ErrorCode::DatabaseBusy => {
230 ErrorHandling::convert(PlacesApiError::PlacesConnectionBusy {
231 reason: self.to_string(),
232 })
233 .log_warning()
234 }
235 rusqlite::ErrorCode::OperationInterrupted => {
236 ErrorHandling::convert(PlacesApiError::OperationInterrupted {
237 reason: self.to_string(),
238 })
239 .log_info()
240 }
241 rusqlite::ErrorCode::DatabaseCorrupt => {
242 ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
243 reason: self.to_string(),
244 })
245 .report_error("places-db-corrupt")
246 }
247 rusqlite::ErrorCode::DiskFull => {
248 ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
249 reason: self.to_string(),
250 })
251 .report_error("places-db-disk-full")
252 }
253 _ => ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
254 reason: self.to_string(),
255 })
256 .report_error("places-unexpected"),
257 },
258 Error::InterruptedError(err) => {
259 ErrorHandling::convert(PlacesApiError::OperationInterrupted {
261 reason: err.to_string(),
262 })
263 .log_info()
264 }
265 Error::Corruption(e) => {
266 ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
267 reason: e.to_string(),
268 })
269 .report_error("places-bookmarks-corruption")
270 }
271 Error::InvalidMetadataObservation(InvalidMetadataObservation::ViewTimeTooLong) => {
272 ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
273 reason: self.to_string(),
274 })
275 .log_warning()
276 }
277 _ => ErrorHandling::convert(PlacesApiError::UnexpectedPlacesException {
278 reason: self.to_string(),
279 })
280 .report_error("places-unexpected-error"),
281 }
282 }
283}