1use thiserror::Error;
2
3use super::*;
4
5#[derive(Error, Debug)]
6#[repr(C)]
7pub enum Error {
8 #[error("no such key")]
9 NoSuchKey,
10 #[error("sqlite error: {0:?}")]
11 Sqlite(#[from] rusqlite::Error),
12 #[error("io error: {0:?}")]
13 Io(#[from] std::io::Error),
14 #[error("anyhow: {0:?}")]
15 Anyhow(#[from] anyhow::Error),
18 #[error("unsupported filesystem")]
19 UnsupportedFilesystem,
20}
21
22use Error::*;
23
24pub trait FileAlreadyExistsError {
25 fn is_file_already_exists(&self) -> bool;
26}
27
28impl FileAlreadyExistsError for std::io::Error {
29 fn is_file_already_exists(&self) -> bool {
30 self.kind() == ErrorKind::AlreadyExists
31 }
32}
33
34impl FileAlreadyExistsError for Error {
35 fn is_file_already_exists(&self) -> bool {
38 match self {
39 Io(err) => err.is_file_already_exists(),
40 _ => false,
41 }
42 }
43}
44
45impl Error {
46 pub fn root_cause(&self) -> &(dyn std::error::Error + 'static) {
47 match self {
48 NoSuchKey | UnsupportedFilesystem => self,
49 Sqlite(inner) => inner,
50 Anyhow(inner) => inner.root_cause(),
51 _ => unimplemented!(),
52 }
53 }
54
55 pub fn root_cause_is_unsupported_filesystem(&self) -> bool {
56 matches!(
57 self.root_cause().downcast_ref(),
58 Some(Self::UnsupportedFilesystem)
59 )
60 }
61}
62
63#[cfg(windows)]
64impl From<windows::core::Error> for Error {
65 fn from(from: windows::core::Error) -> Self {
66 anyhow::Error::from(from).into()
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use anyhow::Context;
73
74 use crate::{Error, PubResult};
75
76 #[test]
77 fn test_downcast_double_contexted() {
78 let res = Err::<(), _>(Error::UnsupportedFilesystem);
79 let res: PubResult<_> = res.context("sup").map_err(Into::into);
80 let res: PubResult<()> = res.context("bro").map_err(Into::into);
81 let err: Error = res.unwrap_err();
82 assert!(matches!(
83 err.root_cause().downcast_ref::<Error>(),
84 Some(Error::UnsupportedFilesystem)
85 ));
86 assert!(err.root_cause_is_unsupported_filesystem());
87 }
88}