+277 -148 +/-16 browse
1 | diff --git a/Cargo.lock b/Cargo.lock |
2 | index d9c0270..f0373f1 100644 |
3 | --- a/Cargo.lock |
4 | +++ b/Cargo.lock |
5 | @@ -257,7 +257,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" |
6 | dependencies = [ |
7 | "proc-macro2", |
8 | "quote", |
9 | - "syn 2.0.15", |
10 | + "syn 2.0.35", |
11 | ] |
12 | |
13 | [[package]] |
14 | @@ -274,7 +274,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" |
15 | dependencies = [ |
16 | "proc-macro2", |
17 | "quote", |
18 | - "syn 2.0.15", |
19 | + "syn 2.0.35", |
20 | ] |
21 | |
22 | [[package]] |
23 | @@ -409,7 +409,7 @@ dependencies = [ |
24 | "heck", |
25 | "proc-macro2", |
26 | "quote", |
27 | - "syn 2.0.15", |
28 | + "syn 2.0.35", |
29 | ] |
30 | |
31 | [[package]] |
32 | @@ -650,7 +650,7 @@ dependencies = [ |
33 | "proc-macro2", |
34 | "quote", |
35 | "serde_json", |
36 | - "syn 2.0.15", |
37 | + "syn 2.0.35", |
38 | "xz2", |
39 | ] |
40 | |
41 | @@ -793,7 +793,7 @@ dependencies = [ |
42 | "heck", |
43 | "proc-macro2", |
44 | "quote", |
45 | - "syn 2.0.15", |
46 | + "syn 2.0.35", |
47 | ] |
48 | |
49 | [[package]] |
50 | @@ -976,7 +976,7 @@ dependencies = [ |
51 | "proc-macro2", |
52 | "quote", |
53 | "scratch", |
54 | - "syn 2.0.15", |
55 | + "syn 2.0.35", |
56 | ] |
57 | |
58 | [[package]] |
59 | @@ -993,7 +993,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" |
60 | dependencies = [ |
61 | "proc-macro2", |
62 | "quote", |
63 | - "syn 2.0.15", |
64 | + "syn 2.0.35", |
65 | ] |
66 | |
67 | [[package]] |
68 | @@ -1166,15 +1166,6 @@ dependencies = [ |
69 | ] |
70 | |
71 | [[package]] |
72 | - name = "error-chain" |
73 | - version = "0.12.4" |
74 | - source = "registry+https://github.com/rust-lang/crates.io-index" |
75 | - checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" |
76 | - dependencies = [ |
77 | - "version_check", |
78 | - ] |
79 | - |
80 | - [[package]] |
81 | name = "event-listener" |
82 | version = "2.5.3" |
83 | source = "registry+https://github.com/rust-lang/crates.io-index" |
84 | @@ -1388,7 +1379,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" |
85 | dependencies = [ |
86 | "proc-macro2", |
87 | "quote", |
88 | - "syn 2.0.15", |
89 | + "syn 2.0.35", |
90 | ] |
91 | |
92 | [[package]] |
93 | @@ -2014,7 +2005,6 @@ version = "0.1.1" |
94 | dependencies = [ |
95 | "anyhow", |
96 | "chrono", |
97 | - "error-chain", |
98 | "jsonschema", |
99 | "log", |
100 | "mailpot-tests", |
101 | @@ -2027,6 +2017,7 @@ dependencies = [ |
102 | "serde_json", |
103 | "stderrlog", |
104 | "tempfile", |
105 | + "thiserror", |
106 | "toml", |
107 | "xdg", |
108 | ] |
109 | @@ -2476,7 +2467,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" |
110 | dependencies = [ |
111 | "proc-macro2", |
112 | "quote", |
113 | - "syn 2.0.15", |
114 | + "syn 2.0.35", |
115 | ] |
116 | |
117 | [[package]] |
118 | @@ -2587,7 +2578,7 @@ dependencies = [ |
119 | "pest_meta", |
120 | "proc-macro2", |
121 | "quote", |
122 | - "syn 2.0.15", |
123 | + "syn 2.0.35", |
124 | ] |
125 | |
126 | [[package]] |
127 | @@ -2730,18 +2721,18 @@ dependencies = [ |
128 | |
129 | [[package]] |
130 | name = "proc-macro2" |
131 | - version = "1.0.56" |
132 | + version = "1.0.67" |
133 | source = "registry+https://github.com/rust-lang/crates.io-index" |
134 | - checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" |
135 | + checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" |
136 | dependencies = [ |
137 | "unicode-ident", |
138 | ] |
139 | |
140 | [[package]] |
141 | name = "quote" |
142 | - version = "1.0.27" |
143 | + version = "1.0.33" |
144 | source = "registry+https://github.com/rust-lang/crates.io-index" |
145 | - checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" |
146 | + checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" |
147 | dependencies = [ |
148 | "proc-macro2", |
149 | ] |
150 | @@ -3078,7 +3069,7 @@ checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" |
151 | dependencies = [ |
152 | "proc-macro2", |
153 | "quote", |
154 | - "syn 2.0.15", |
155 | + "syn 2.0.35", |
156 | ] |
157 | |
158 | [[package]] |
159 | @@ -3275,9 +3266,9 @@ dependencies = [ |
160 | |
161 | [[package]] |
162 | name = "syn" |
163 | - version = "2.0.15" |
164 | + version = "2.0.35" |
165 | source = "registry+https://github.com/rust-lang/crates.io-index" |
166 | - checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" |
167 | + checksum = "59bf04c28bee9043ed9ea1e41afc0552288d3aba9c6efdd78903b802926f4879" |
168 | dependencies = [ |
169 | "proc-macro2", |
170 | "quote", |
171 | @@ -3336,22 +3327,22 @@ checksum = "9d4ae32d0a4605a89c28534371b056919c12e7a070ee07505af75130ff030111" |
172 | |
173 | [[package]] |
174 | name = "thiserror" |
175 | - version = "1.0.40" |
176 | + version = "1.0.48" |
177 | source = "registry+https://github.com/rust-lang/crates.io-index" |
178 | - checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" |
179 | + checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" |
180 | dependencies = [ |
181 | "thiserror-impl", |
182 | ] |
183 | |
184 | [[package]] |
185 | name = "thiserror-impl" |
186 | - version = "1.0.40" |
187 | + version = "1.0.48" |
188 | source = "registry+https://github.com/rust-lang/crates.io-index" |
189 | - checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" |
190 | + checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" |
191 | dependencies = [ |
192 | "proc-macro2", |
193 | "quote", |
194 | - "syn 2.0.15", |
195 | + "syn 2.0.35", |
196 | ] |
197 | |
198 | [[package]] |
199 | @@ -3444,7 +3435,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" |
200 | dependencies = [ |
201 | "proc-macro2", |
202 | "quote", |
203 | - "syn 2.0.15", |
204 | + "syn 2.0.35", |
205 | ] |
206 | |
207 | [[package]] |
208 | @@ -3570,7 +3561,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" |
209 | dependencies = [ |
210 | "proc-macro2", |
211 | "quote", |
212 | - "syn 2.0.15", |
213 | + "syn 2.0.35", |
214 | ] |
215 | |
216 | [[package]] |
217 | diff --git a/core/Cargo.toml b/core/Cargo.toml |
218 | index 4a598b5..9ca1dca 100644 |
219 | --- a/core/Cargo.toml |
220 | +++ b/core/Cargo.toml |
221 | @@ -16,7 +16,6 @@ doc-scrape-examples = true |
222 | [dependencies] |
223 | anyhow = "1.0.58" |
224 | chrono = { version = "^0.4", features = ["serde", ] } |
225 | - error-chain = { version = "0.12.4", default-features = false } |
226 | jsonschema = { version = "0.17", default-features = false } |
227 | log = "0.4" |
228 | melib = { version = "*", default-features = false, features = ["smtp", "unicode_algorithms", "maildir_backend"], git = "https://github.com/meli/meli", rev = "2447a2c" } |
229 | @@ -25,6 +24,7 @@ percent-encoding = { version = "^2.1" } |
230 | rusqlite = { version = "^0.28", features = ["bundled", "functions", "trace", "hooks", "serde_json", "array", "chrono", "unlock_notify"] } |
231 | serde = { version = "^1", features = ["derive", ] } |
232 | serde_json = "^1" |
233 | + thiserror = { version = "1.0.48", default-features = false } |
234 | toml = "^0.5" |
235 | xdg = "2.4.1" |
236 | |
237 | diff --git a/core/make_migrations.rs b/core/make_migrations.rs |
238 | index 011970a..4b4acbe 100644 |
239 | --- a/core/make_migrations.rs |
240 | +++ b/core/make_migrations.rs |
241 | @@ -86,15 +86,15 @@ pub fn make_migrations<M: AsRef<Path>, O: AsRef<Path>>( |
242 | migr_rs |
243 | .write_all(num.trim_start_matches('0').as_bytes()) |
244 | .unwrap(); |
245 | - migr_rs.write_all(b",r###\"").unwrap(); |
246 | + migr_rs.write_all(b",r##\"").unwrap(); |
247 | |
248 | let redo = std::fs::read_to_string(p).unwrap(); |
249 | migr_rs.write_all(redo.trim().as_bytes()).unwrap(); |
250 | - migr_rs.write_all(b"\"###,r###\"").unwrap(); |
251 | + migr_rs.write_all(b"\"##,r##\"").unwrap(); |
252 | migr_rs |
253 | .write_all(std::fs::read_to_string(u).unwrap().trim().as_bytes()) |
254 | .unwrap(); |
255 | - migr_rs.write_all(b"\"###),").unwrap(); |
256 | + migr_rs.write_all(b"\"##),").unwrap(); |
257 | if is_data { |
258 | schema_file.extend(b"\n\n-- ".iter()); |
259 | schema_file.extend(num.as_bytes().iter()); |
260 | diff --git a/core/src/errors.rs b/core/src/errors.rs |
261 | index 8aebb2a..ef37006 100644 |
262 | --- a/core/src/errors.rs |
263 | +++ b/core/src/errors.rs |
264 | @@ -19,58 +19,184 @@ |
265 | |
266 | //! Errors of this library. |
267 | |
268 | - pub use error_chain::ChainedError; |
269 | + use std::sync::Arc; |
270 | + |
271 | + use thiserror::Error; |
272 | |
273 | pub use crate::anyhow::Context; |
274 | |
275 | - // Create the Error, ErrorKind, ResultExt, and Result types |
276 | - |
277 | - error_chain! { |
278 | - errors { |
279 | - /// Post rejected. |
280 | - PostRejected(reason: String) { |
281 | - description("Post rejected") |
282 | - display("Your post has been rejected: {}", reason) |
283 | - } |
284 | - |
285 | - /// An entry was not found in the database. |
286 | - NotFound(model: &'static str) { |
287 | - description("Not found") |
288 | - display("This {} is not present in the database.", model) |
289 | - } |
290 | - |
291 | - /// A request was invalid. |
292 | - InvalidRequest(reason: String) { |
293 | - description("List request is invalid") |
294 | - display("Your list request has been found invalid: {}.", reason) |
295 | - } |
296 | - |
297 | - /// An error happened and it was handled internally. |
298 | - Information(reason: String) { |
299 | - description("An error happened and it was handled internally.") |
300 | - display("{}.", reason) |
301 | - } |
302 | - |
303 | - /// An error that shouldn't happen and should be reported. |
304 | - Bug(reason: String) { |
305 | - description("An error that shouldn't happen and should be reported.") |
306 | - display("{}.", reason) |
307 | - } |
308 | - } |
309 | - foreign_links { |
310 | - External(anyhow::Error) #[doc="Error returned from an external user initiated operation such as deserialization or I/O."]; |
311 | - Sql(rusqlite::Error) #[doc="Error returned from sqlite3."]; |
312 | - Io(::std::io::Error) #[doc="Error returned from internal I/O operations."]; |
313 | - Melib(melib::error::Error) #[doc="Error returned from e-mail protocol operations from `melib` crate."]; |
314 | - SerdeJson(serde_json::Error) #[doc="Error from deserializing JSON values."]; |
315 | - Template(minijinja::Error) #[doc="Error returned from minijinja template engine."]; |
316 | - } |
317 | + /// Mailpot library error. |
318 | + #[derive(Error, Debug)] |
319 | + pub struct Error { |
320 | + kind: ErrorKind, |
321 | + source: Option<Arc<Self>>, |
322 | + } |
323 | + |
324 | + /// Mailpot library error. |
325 | + #[derive(Error, Debug)] |
326 | + pub enum ErrorKind { |
327 | + /// Post rejected. |
328 | + #[error("Your post has been rejected: {0}")] |
329 | + PostRejected(String), |
330 | + /// An entry was not found in the database. |
331 | + #[error("This {0} is not present in the database.")] |
332 | + NotFound(&'static str), |
333 | + /// A request was invalid. |
334 | + #[error("Your list request has been found invalid: {0}.")] |
335 | + InvalidRequest(String), |
336 | + /// An error happened and it was handled internally. |
337 | + #[error("An error happened and it was handled internally: {0}.")] |
338 | + Information(String), |
339 | + /// An error that shouldn't happen and should be reported. |
340 | + #[error("An error that shouldn't happen and should be reported: {0}.")] |
341 | + Bug(String), |
342 | + |
343 | + /// Error returned from an external user initiated operation such as |
344 | + /// deserialization or I/O. |
345 | + #[error( |
346 | + "Error returned from an external user initiated operation such as deserialization or I/O. \ |
347 | + {0}" |
348 | + )] |
349 | + External(#[from] anyhow::Error), |
350 | + /// Generic |
351 | + #[error("{0}")] |
352 | + Generic(anyhow::Error), |
353 | + /// Error returned from sqlite3. |
354 | + #[error("Error returned from sqlite3 {0}.")] |
355 | + Sql( |
356 | + #[from] |
357 | + #[source] |
358 | + rusqlite::Error, |
359 | + ), |
360 | + /// Error returned from sqlite3. |
361 | + #[error("Error returned from sqlite3. {0}")] |
362 | + SqlLib( |
363 | + #[from] |
364 | + #[source] |
365 | + rusqlite::ffi::Error, |
366 | + ), |
367 | + /// Error returned from internal I/O operations. |
368 | + #[error("Error returned from internal I/O operations. {0}")] |
369 | + Io(#[from] ::std::io::Error), |
370 | + /// Error returned from e-mail protocol operations from `melib` crate. |
371 | + #[error("Error returned from e-mail protocol operations from `melib` crate. {0}")] |
372 | + Melib(#[from] melib::error::Error), |
373 | + /// Error from deserializing JSON values. |
374 | + #[error("Error from deserializing JSON values. {0}")] |
375 | + SerdeJson(#[from] serde_json::Error), |
376 | + /// Error returned from minijinja template engine. |
377 | + #[error("Error returned from minijinja template engine. {0}")] |
378 | + Template(#[from] minijinja::Error), |
379 | + } |
380 | + |
381 | + impl std::fmt::Display for Error { |
382 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
383 | + write!(fmt, "{}", self.kind) |
384 | + } |
385 | + } |
386 | + |
387 | + impl From<ErrorKind> for Error { |
388 | + fn from(kind: ErrorKind) -> Self { |
389 | + Self { kind, source: None } |
390 | + } |
391 | + } |
392 | + |
393 | + macro_rules! impl_from { |
394 | + ($ty:ty) => { |
395 | + impl From<$ty> for Error { |
396 | + fn from(err: $ty) -> Self { |
397 | + Self { |
398 | + kind: err.into(), |
399 | + source: None, |
400 | + } |
401 | + } |
402 | + } |
403 | + }; |
404 | } |
405 | |
406 | + impl_from! { anyhow::Error } |
407 | + impl_from! { rusqlite::Error } |
408 | + impl_from! { rusqlite::ffi::Error } |
409 | + impl_from! { ::std::io::Error } |
410 | + impl_from! { melib::error::Error } |
411 | + impl_from! { serde_json::Error } |
412 | + impl_from! { minijinja::Error } |
413 | + |
414 | impl Error { |
415 | /// Helper function to create a new generic error message. |
416 | pub fn new_external<S: Into<String>>(msg: S) -> Self { |
417 | let msg = msg.into(); |
418 | - Self::from(ErrorKind::External(anyhow::Error::msg(msg))) |
419 | + ErrorKind::External(anyhow::Error::msg(msg)).into() |
420 | + } |
421 | + |
422 | + /// Chain an error by introducing a new head of the error chain. |
423 | + pub fn chain_err<E>(self, lambda: impl FnOnce() -> E) -> Self |
424 | + where |
425 | + E: Into<Self>, |
426 | + { |
427 | + let new_head: Self = lambda().into(); |
428 | + Self { |
429 | + source: Some(Arc::new(self)), |
430 | + ..new_head |
431 | + } |
432 | + } |
433 | + |
434 | + /// Insert a source error into this Error. |
435 | + pub fn with_source<E>(self, source: E) -> Self |
436 | + where |
437 | + E: Into<Self>, |
438 | + { |
439 | + Self { |
440 | + source: Some(Arc::new(source.into())), |
441 | + ..self |
442 | + } |
443 | + } |
444 | + |
445 | + /// Getter for the kind field. |
446 | + pub fn kind(&self) -> &ErrorKind { |
447 | + &self.kind |
448 | + } |
449 | + |
450 | + /// Display error chain to user. |
451 | + pub fn display_chain(&'_ self) -> impl std::fmt::Display + '_ { |
452 | + ErrorChainDisplay { |
453 | + current: self, |
454 | + counter: 1, |
455 | + } |
456 | + } |
457 | + } |
458 | + |
459 | + impl From<String> for Error { |
460 | + fn from(s: String) -> Self { |
461 | + ErrorKind::Generic(anyhow::Error::msg(s)).into() |
462 | + } |
463 | + } |
464 | + impl From<&str> for Error { |
465 | + fn from(s: &str) -> Self { |
466 | + ErrorKind::Generic(anyhow::Error::msg(s.to_string())).into() |
467 | + } |
468 | + } |
469 | + |
470 | + /// Type alias for Mailpot library Results. |
471 | + pub type Result<T> = std::result::Result<T, Error>; |
472 | + |
473 | + struct ErrorChainDisplay<'e> { |
474 | + current: &'e Error, |
475 | + counter: usize, |
476 | + } |
477 | + |
478 | + impl std::fmt::Display for ErrorChainDisplay<'_> { |
479 | + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
480 | + if let Some(ref source) = self.current.source { |
481 | + writeln!(fmt, "[{}] {}, caused by:", self.counter, self.current.kind)?; |
482 | + Self { |
483 | + current: source, |
484 | + counter: self.counter + 1, |
485 | + } |
486 | + .fmt(fmt) |
487 | + } else { |
488 | + writeln!(fmt, "[{}] {}", self.counter, self.current.kind)?; |
489 | + Ok(()) |
490 | + } |
491 | } |
492 | } |
493 | diff --git a/core/src/lib.rs b/core/src/lib.rs |
494 | index 55f28a4..4f30b93 100644 |
495 | --- a/core/src/lib.rs |
496 | +++ b/core/src/lib.rs |
497 | @@ -151,9 +151,6 @@ |
498 | * - [tag:VERIFY] Verify whether this is the correct way to do something |
499 | */ |
500 | |
501 | - #[macro_use] |
502 | - extern crate error_chain; |
503 | - |
504 | /// Error library |
505 | pub extern crate anyhow; |
506 | /// Date library |
507 | diff --git a/core/src/message_filters.rs b/core/src/message_filters.rs |
508 | index dd51d2a..7136f8e 100644 |
509 | --- a/core/src/message_filters.rs |
510 | +++ b/core/src/message_filters.rs |
511 | @@ -282,7 +282,10 @@ impl PostFilter for ArchivedAtLink { |
512 | ctx: &'p mut ListContext<'list>, |
513 | ) -> std::result::Result<(&'p mut PostEntry, &'p mut ListContext<'list>), ()> { |
514 | let Some(mut settings) = ctx.filter_settings.remove("ArchivedAtLinkSettings") else { |
515 | - trace!("No ArchivedAtLink settings found for list.pk = {} skipping filter", ctx.list.pk); |
516 | + trace!( |
517 | + "No ArchivedAtLink settings found for list.pk = {} skipping filter", |
518 | + ctx.list.pk |
519 | + ); |
520 | return Ok((post, ctx)); |
521 | }; |
522 | trace!("Running ArchivedAtLink filter"); |
523 | @@ -392,8 +395,7 @@ impl PostFilter for MimeReject { |
524 | let enabled = serde_json::from_value::<bool>(map.remove("enabled").unwrap()).unwrap(); |
525 | if !enabled { |
526 | trace!( |
527 | - "MimeReject is disabled from settings found for list.pk = {} \ |
528 | - skipping filter", |
529 | + "MimeReject is disabled from settings found for list.pk = {} skipping filter", |
530 | ctx.list.pk |
531 | ); |
532 | return Ok((post, ctx)); |
533 | diff --git a/core/src/migrations.rs.inc b/core/src/migrations.rs.inc |
534 | index 7f9f7ab..aa1a2d6 100644 |
535 | --- a/core/src/migrations.rs.inc |
536 | +++ b/core/src/migrations.rs.inc |
537 | @@ -1,10 +1,10 @@ |
538 | |
539 | //(user_version, redo sql, undo sql |
540 | - &[(1,r###"PRAGMA foreign_keys=ON; |
541 | - ALTER TABLE templates RENAME TO template;"###,r###"PRAGMA foreign_keys=ON; |
542 | - ALTER TABLE template RENAME TO templates;"###),(2,r###"PRAGMA foreign_keys=ON; |
543 | - ALTER TABLE list ADD COLUMN topics JSON NOT NULL CHECK (json_type(topics) == 'array') DEFAULT '[]';"###,r###"PRAGMA foreign_keys=ON; |
544 | - ALTER TABLE list DROP COLUMN topics;"###),(3,r###"PRAGMA foreign_keys=ON; |
545 | + &[(1,r##"PRAGMA foreign_keys=ON; |
546 | + ALTER TABLE templates RENAME TO template;"##,r##"PRAGMA foreign_keys=ON; |
547 | + ALTER TABLE template RENAME TO templates;"##),(2,r##"PRAGMA foreign_keys=ON; |
548 | + ALTER TABLE list ADD COLUMN topics JSON NOT NULL CHECK (json_type(topics) == 'array') DEFAULT '[]';"##,r##"PRAGMA foreign_keys=ON; |
549 | + ALTER TABLE list DROP COLUMN topics;"##),(3,r##"PRAGMA foreign_keys=ON; |
550 | |
551 | UPDATE list SET topics = arr FROM (SELECT json_group_array(ord.val) AS arr, ord.pk AS pk FROM (SELECT json_each.value AS val, list.pk AS pk FROM list, json_each(list.topics) ORDER BY val ASC) AS ord GROUP BY pk) AS ord WHERE ord.pk = list.pk; |
552 | |
553 | @@ -23,10 +23,10 @@ AFTER INSERT ON list |
554 | FOR EACH ROW |
555 | BEGIN |
556 | UPDATE list SET topics = arr FROM (SELECT json_group_array(ord.val) AS arr, ord.pk AS pk FROM (SELECT json_each.value AS val, list.pk AS pk FROM list, json_each(list.topics) ORDER BY val ASC) AS ord GROUP BY pk) AS ord WHERE ord.pk = list.pk AND list.pk = NEW.pk; |
557 | - END;"###,r###"PRAGMA foreign_keys=ON; |
558 | + END;"##,r##"PRAGMA foreign_keys=ON; |
559 | |
560 | DROP TRIGGER sort_topics_update_trigger; |
561 | - DROP TRIGGER sort_topics_new_trigger;"###),(4,r###"CREATE TABLE IF NOT EXISTS settings_json_schema ( |
562 | + DROP TRIGGER sort_topics_new_trigger;"##),(4,r##"CREATE TABLE IF NOT EXISTS settings_json_schema ( |
563 | pk INTEGER PRIMARY KEY NOT NULL, |
564 | id TEXT NOT NULL UNIQUE, |
565 | value JSON NOT NULL CHECK (json_type(value) = 'object'), |
566 | @@ -192,8 +192,8 @@ WHEN NEW.last_modified == OLD.last_modified |
567 | BEGIN |
568 | UPDATE list_settings_json SET last_modified = unixepoch() |
569 | WHERE pk = NEW.pk; |
570 | - END;"###,r###"DROP TABLE settings_json_schema; |
571 | - DROP TABLE list_settings_json;"###),(5,r###"INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('ArchivedAtLinkSettings', '{ |
572 | + END;"##,r##"DROP TABLE settings_json_schema; |
573 | + DROP TABLE list_settings_json;"##),(5,r##"INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('ArchivedAtLinkSettings', '{ |
574 | "$schema": "http://json-schema.org/draft-07/schema", |
575 | "$ref": "#/$defs/ArchivedAtLinkSettings", |
576 | "$defs": { |
577 | @@ -223,7 +223,7 @@ DROP TABLE list_settings_json;"###),(5,r###"INSERT OR REPLACE INTO settings_json |
578 | ] |
579 | } |
580 | } |
581 | - }');"###,r###"DELETE FROM settings_json_schema WHERE id = 'ArchivedAtLinkSettings';"###),(6,r###"INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('AddSubjectTagPrefixSettings', '{ |
582 | + }');"##,r##"DELETE FROM settings_json_schema WHERE id = 'ArchivedAtLinkSettings';"##),(6,r##"INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('AddSubjectTagPrefixSettings', '{ |
583 | "$schema": "http://json-schema.org/draft-07/schema", |
584 | "$ref": "#/$defs/AddSubjectTagPrefixSettings", |
585 | "$defs": { |
586 | @@ -242,7 +242,7 @@ DROP TABLE list_settings_json;"###),(5,r###"INSERT OR REPLACE INTO settings_json |
587 | ] |
588 | } |
589 | } |
590 | - }');"###,r###"DELETE FROM settings_json_schema WHERE id = 'AddSubjectTagPrefixSettings';"###),(7,r###"INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('MimeRejectSettings', '{ |
591 | + }');"##,r##"DELETE FROM settings_json_schema WHERE id = 'AddSubjectTagPrefixSettings';"##),(7,r##"INSERT OR REPLACE INTO settings_json_schema(id, value) VALUES('MimeRejectSettings', '{ |
592 | "$schema": "http://json-schema.org/draft-07/schema", |
593 | "$ref": "#/$defs/MimeRejectSettings", |
594 | "$defs": { |
595 | @@ -274,4 +274,4 @@ DROP TABLE list_settings_json;"###),(5,r###"INSERT OR REPLACE INTO settings_json |
596 | "pattern": "^[a-zA-Z!#$&-^_]+[/][a-zA-Z!#$&-^_]+$" |
597 | } |
598 | } |
599 | - }');"###,r###"DELETE FROM settings_json_schema WHERE id = 'MimeRejectSettings';"###),] |
600 | \ No newline at end of file |
601 | + }');"##,r##"DELETE FROM settings_json_schema WHERE id = 'MimeRejectSettings';"##),] |
602 | \ No newline at end of file |
603 | diff --git a/core/src/models.rs b/core/src/models.rs |
604 | index daa4575..5348eec 100644 |
605 | --- a/core/src/models.rs |
606 | +++ b/core/src/models.rs |
607 | @@ -42,9 +42,9 @@ use melib::email::Address; |
608 | /// ``` |
609 | #[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize)] |
610 | #[serde(transparent)] |
611 | - pub struct DbVal<T>(pub T, #[serde(skip)] pub i64); |
612 | + pub struct DbVal<T: Send + Sync>(pub T, #[serde(skip)] pub i64); |
613 | |
614 | - impl<T> DbVal<T> { |
615 | + impl<T: Send + Sync> DbVal<T> { |
616 | /// Primary key. |
617 | #[inline(always)] |
618 | pub fn pk(&self) -> i64 { |
619 | @@ -60,21 +60,27 @@ impl<T> DbVal<T> { |
620 | |
621 | impl<T> std::borrow::Borrow<T> for DbVal<T> |
622 | where |
623 | - T: Sized, |
624 | + T: Send + Sync + Sized, |
625 | { |
626 | fn borrow(&self) -> &T { |
627 | &self.0 |
628 | } |
629 | } |
630 | |
631 | - impl<T> std::ops::Deref for DbVal<T> { |
632 | + impl<T> std::ops::Deref for DbVal<T> |
633 | + where |
634 | + T: Send + Sync, |
635 | + { |
636 | type Target = T; |
637 | fn deref(&self) -> &T { |
638 | &self.0 |
639 | } |
640 | } |
641 | |
642 | - impl<T> std::ops::DerefMut for DbVal<T> { |
643 | + impl<T> std::ops::DerefMut for DbVal<T> |
644 | + where |
645 | + T: Send + Sync, |
646 | + { |
647 | fn deref_mut(&mut self) -> &mut Self::Target { |
648 | &mut self.0 |
649 | } |
650 | @@ -82,7 +88,7 @@ impl<T> std::ops::DerefMut for DbVal<T> { |
651 | |
652 | impl<T> std::fmt::Display for DbVal<T> |
653 | where |
654 | - T: std::fmt::Display, |
655 | + T: std::fmt::Display + Send + Sync, |
656 | { |
657 | fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { |
658 | write!(fmt, "{}", self.0) |
659 | diff --git a/core/src/policies.rs b/core/src/policies.rs |
660 | index 259c796..432495b 100644 |
661 | --- a/core/src/policies.rs |
662 | +++ b/core/src/policies.rs |
663 | @@ -154,10 +154,9 @@ mod post_policy { |
664 | || policy.open |
665 | || policy.custom) |
666 | { |
667 | - return Err( |
668 | - "Cannot add empty policy. Having no policies is probably what you want to do." |
669 | - .into(), |
670 | - ); |
671 | + return Err(Error::new_external( |
672 | + "Cannot add empty policy. Having no policies is probably what you want to do.", |
673 | + )); |
674 | } |
675 | let list_pk = policy.list; |
676 | |
677 | @@ -347,10 +346,9 @@ mod subscription_policy { |
678 | policy: SubscriptionPolicy, |
679 | ) -> Result<DbVal<SubscriptionPolicy>> { |
680 | if !(policy.open || policy.manual || policy.request || policy.custom) { |
681 | - return Err( |
682 | - "Cannot add empty policy. Having no policy is probably what you want to do." |
683 | - .into(), |
684 | - ); |
685 | + return Err(Error::new_external( |
686 | + "Cannot add empty policy. Having no policy is probably what you want to do.", |
687 | + )); |
688 | } |
689 | let list_pk = policy.list; |
690 | |
691 | diff --git a/core/src/postfix.rs b/core/src/postfix.rs |
692 | index 4673d74..0cb7b05 100644 |
693 | --- a/core/src/postfix.rs |
694 | +++ b/core/src/postfix.rs |
695 | @@ -142,7 +142,9 @@ flags=RX user={username}{group_sep}{groupname} directory={{{data_dir}}} argv={{{ |
696 | } |
697 | }; |
698 | |
699 | - let Some(width): Option<usize> = lists.iter().map(|(l, p)| calc_width(l, p.as_deref())).max() else { |
700 | + let Some(width): Option<usize> = |
701 | + lists.iter().map(|(l, p)| calc_width(l, p.as_deref())).max() |
702 | + else { |
703 | return ret; |
704 | }; |
705 | |
706 | @@ -281,7 +283,9 @@ flags=RX user={username}{group_sep}{groupname} directory={{{data_dir}}} argv={{{ |
707 | pub fn save_maps(&self, config: &Configuration) -> Result<()> { |
708 | let db = Connection::open_db(config.clone())?; |
709 | let Some(postmap) = find_binary_in_path("postmap") else { |
710 | - return Err(Error::from(ErrorKind::External(anyhow::Error::msg("Could not find postmap binary in PATH.")))); |
711 | + return Err(Error::from(ErrorKind::External(anyhow::Error::msg( |
712 | + "Could not find postmap binary in PATH.", |
713 | + )))); |
714 | }; |
715 | let lists = db.lists()?; |
716 | let lists_post_policies = lists |
717 | diff --git a/core/src/posts.rs b/core/src/posts.rs |
718 | index fa00cfc..777d09f 100644 |
719 | --- a/core/src/posts.rs |
720 | +++ b/core/src/posts.rs |
721 | @@ -195,9 +195,7 @@ impl Connection { |
722 | }; |
723 | let result = filters |
724 | .into_iter() |
725 | - .fold(Ok((&mut post, &mut list_ctx)), |p, f| { |
726 | - p.and_then(|(p, c)| f.feed(p, c)) |
727 | - }); |
728 | + .try_fold((&mut post, &mut list_ctx), |(p, c), f| f.feed(p, c)); |
729 | trace!("result {:#?}", result); |
730 | |
731 | let PostEntry { bytes, action, .. } = post; |
732 | diff --git a/core/tests/authorizer.rs b/core/tests/authorizer.rs |
733 | index b5fa1ca..f4e124a 100644 |
734 | --- a/core/tests/authorizer.rs |
735 | +++ b/core/tests/authorizer.rs |
736 | @@ -17,9 +17,7 @@ |
737 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
738 | */ |
739 | |
740 | - use std::error::Error; |
741 | - |
742 | - use mailpot::{models::*, Configuration, Connection, SendMail}; |
743 | + use mailpot::{models::*, Configuration, Connection, ErrorKind, SendMail}; |
744 | use mailpot_tests::init_stderr_logging; |
745 | use tempfile::TempDir; |
746 | |
747 | @@ -64,14 +62,15 @@ fn test_authorizer() { |
748 | .unwrap_err(), |
749 | ] { |
750 | assert_eq!( |
751 | - err.source() |
752 | - .unwrap() |
753 | - .downcast_ref::<rusqlite::ffi::Error>() |
754 | - .unwrap(), |
755 | - &rusqlite::ffi::Error { |
756 | - code: rusqlite::ErrorCode::AuthorizationForStatementDenied, |
757 | - extended_code: 23 |
758 | - }, |
759 | + err.kind().to_string(), |
760 | + ErrorKind::Sql(rusqlite::Error::SqliteFailure( |
761 | + rusqlite::ffi::Error { |
762 | + code: rusqlite::ErrorCode::AuthorizationForStatementDenied, |
763 | + extended_code: 23, |
764 | + }, |
765 | + Some("not authorized".into()), |
766 | + )) |
767 | + .to_string() |
768 | ); |
769 | } |
770 | assert!(db.lists().unwrap().is_empty()); |
771 | diff --git a/core/tests/migrations.rs b/core/tests/migrations.rs |
772 | index 25fa17e..2cf7859 100644 |
773 | --- a/core/tests/migrations.rs |
774 | +++ b/core/tests/migrations.rs |
775 | @@ -185,8 +185,8 @@ fn test_migration_gen() { |
776 | |
777 | make_migrations(&in_path, &out_path, &mut vec![]); |
778 | let output = std::fs::read_to_string(&out_path).unwrap(); |
779 | - assert_eq!(&output.replace([' ', '\n'], ""), &r####"//(user_version, redo sql, undo sql |
780 | - &[(1,r###"ALTER TABLE PERSON ADD COLUMN interests TEXT;"###,r###"ALTER TABLE PERSON DROP COLUMN interests;"###),(2,r###"CREATE TABLE hobby ( pk INTEGER PRIMARY KEY NOT NULL,title TEXT NOT NULL);"###,r###"DROP TABLE hobby;"###),(3,r###"ALTER TABLE PERSON ADD COLUMN main_hobby INTEGER REFERENCES hobby(pk) ON DELETE SET NULL;"###,r###"ALTER TABLE PERSON DROP COLUMN main_hobby;"###),]"####.replace([' ', '\n'], "")); |
781 | + assert_eq!(&output.replace([' ', '\n'], ""), &r###"//(user_version, redo sql, undo sql |
782 | + &[(1,r##"ALTER TABLE PERSON ADD COLUMN interests TEXT;"##,r##"ALTER TABLE PERSON DROP COLUMN interests;"##),(2,r##"CREATE TABLE hobby ( pk INTEGER PRIMARY KEY NOT NULL,title TEXT NOT NULL);"##,r##"DROP TABLE hobby;"##),(3,r##"ALTER TABLE PERSON ADD COLUMN main_hobby INTEGER REFERENCES hobby(pk) ON DELETE SET NULL;"##,r##"ALTER TABLE PERSON DROP COLUMN main_hobby;"##),]"###.replace([' ', '\n'], "")); |
783 | } |
784 | |
785 | #[test] |
786 | diff --git a/rest-http/src/routes/list.rs b/rest-http/src/routes/list.rs |
787 | index e7f4211..7fdfaad 100644 |
788 | --- a/rest-http/src/routes/list.rs |
789 | +++ b/rest-http/src/routes/list.rs |
790 | @@ -69,16 +69,14 @@ async fn all_lists( |
791 | let lists_values = db.lists()?; |
792 | let page = page.unwrap_or(0); |
793 | let Some(count) = count else { |
794 | - let mut stmt = db |
795 | - .connection |
796 | - .prepare("SELECT count(*) FROM list;")?; |
797 | + let mut stmt = db.connection.prepare("SELECT count(*) FROM list;")?; |
798 | return Ok(Json(GetResponse { |
799 | entries: vec![], |
800 | total: stmt.query_row([], |row| { |
801 | - let count: usize = row.get(0)?; |
802 | - Ok(count) |
803 | - })?, |
804 | - start: 0, |
805 | + let count: usize = row.get(0)?; |
806 | + Ok(count) |
807 | + })?, |
808 | + start: 0, |
809 | })); |
810 | }; |
811 | let offset = page * count; |
812 | diff --git a/web/src/typed_paths.rs b/web/src/typed_paths.rs |
813 | index 9429756..c21656d 100644 |
814 | --- a/web/src/typed_paths.rs |
815 | +++ b/web/src/typed_paths.rs |
816 | @@ -171,29 +171,37 @@ list_id_impl!(list_candidates_path, ListEditCandidatesPath); |
817 | |
818 | macro_rules! list_post_impl { |
819 | ($ident:ident, $ty:tt) => { |
820 | - pub fn $ident(state: &minijinja::State, id: Value, msg_id: Value) -> std::result::Result<Value, Error> { |
821 | + pub fn $ident( |
822 | + state: &minijinja::State, |
823 | + id: Value, |
824 | + msg_id: Value, |
825 | + ) -> std::result::Result<Value, Error> { |
826 | urlize(state, { |
827 | - let Some(msg_id) = msg_id.as_str().map(|s| if s.starts_with('<') && s.ends_with('>') { s.to_string() } else { |
828 | - format!("<{s}>") |
829 | + let Some(msg_id) = msg_id.as_str().map(|s| { |
830 | + if s.starts_with('<') && s.ends_with('>') { |
831 | + s.to_string() |
832 | + } else { |
833 | + format!("<{s}>") |
834 | + } |
835 | }) else { |
836 | return Err(Error::new( |
837 | - minijinja::ErrorKind::UnknownMethod, |
838 | - "Second argument of list_post_path must be a string." |
839 | + minijinja::ErrorKind::UnknownMethod, |
840 | + "Second argument of list_post_path must be a string.", |
841 | )); |
842 | }; |
843 | |
844 | if let Some(id) = id.as_str() { |
845 | Value::from( |
846 | $ty(ListPathIdentifier::Id(id.to_string()), msg_id) |
847 | - .to_crumb() |
848 | - .to_string(), |
849 | + .to_crumb() |
850 | + .to_string(), |
851 | ) |
852 | } else { |
853 | let pk = id.try_into()?; |
854 | Value::from( |
855 | $ty(ListPathIdentifier::Pk(pk), msg_id) |
856 | - .to_crumb() |
857 | - .to_string(), |
858 | + .to_crumb() |
859 | + .to_string(), |
860 | ) |
861 | } |
862 | }) |
863 | diff --git a/web/src/utils.rs b/web/src/utils.rs |
864 | index 4bfa0a4..03f25ac 100644 |
865 | --- a/web/src/utils.rs |
866 | +++ b/web/src/utils.rs |
867 | @@ -485,6 +485,8 @@ pub fn thread_roots( |
868 | tref.date, |
869 | )); |
870 | } |
871 | + // clippy: error: temporary with significant `Drop` can be early dropped |
872 | + drop(env_lock); |
873 | ret.sort_by_key(|(_, _, key)| std::cmp::Reverse(*key)); |
874 | ret |
875 | } |