imessage_database/error/
table.rs1use std::{
6 fmt::{Display, Formatter, Result},
7 path::PathBuf,
8};
9
10use crate::{tables::table::DEFAULT_PATH_IOS, util::path_hint::PathHint};
11
12#[derive(Debug)]
14pub enum TableError {
15 QueryError(rusqlite::Error),
17 CannotConnect(TableConnectError),
19 CannotRead(std::io::Error),
21}
22
23#[derive(Debug)]
25pub enum TableConnectError {
26 Permissions(rusqlite::Error),
28 Inaccessible(PathBuf, std::io::Error),
31 IsDirectory(PathBuf, Vec<PathHint>),
33 MissingBackupDatabase(PathBuf, Vec<PathHint>),
35 NotADatabase(PathBuf),
37 NotMessagesDatabase(PathBuf),
39 DoesNotExist(PathBuf, Vec<PathHint>),
41 NotBackupRoot,
43}
44
45const FULL_DISK_ACCESS: &str = if cfg!(target_os = "macos") {
47 "\nEnsure full disk access is enabled for your terminal emulator in System Settings > Privacy & Security > Full Disk Access"
48} else {
49 ""
50};
51
52fn write_hints(fmt: &mut Formatter<'_>, hints: &[PathHint]) -> Result {
53 for hint in hints {
54 write!(fmt, "\n{hint}")?;
55 }
56 Ok(())
57}
58
59impl Display for TableConnectError {
60 fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
61 match self {
62 TableConnectError::Permissions(why) => write!(
63 fmt,
64 "Unable to read from chat database: {why}{FULL_DISK_ACCESS}"
65 ),
66 TableConnectError::Inaccessible(path, why) => write!(
67 fmt,
68 "Unable to access `{}`: {why}{FULL_DISK_ACCESS}",
69 path.display()
70 ),
71 TableConnectError::IsDirectory(path, hints) => {
72 write!(
73 fmt,
74 "Specified path `{}` is a directory, not a database file!",
75 path.display()
76 )?;
77 write_hints(fmt, hints)
78 }
79 TableConnectError::MissingBackupDatabase(path, hints) => {
80 write!(
81 fmt,
82 "Specified path `{}` is a directory, but it contains no Messages database at `{DEFAULT_PATH_IOS}`!",
83 path.display()
84 )?;
85 write_hints(fmt, hints)
86 }
87 TableConnectError::NotADatabase(path) => write!(
88 fmt,
89 "Specified path `{}` is not a SQLite database file!",
90 path.display()
91 ),
92 TableConnectError::NotMessagesDatabase(path) => write!(
93 fmt,
94 "`{}` is a SQLite database, but it has no `message` table, so it is not a Messages database!",
95 path.display()
96 ),
97 TableConnectError::DoesNotExist(path, hints) => {
98 write!(
99 fmt,
100 "Database file `{}` does not exist at the specified path!",
101 path.display()
102 )?;
103 write_hints(fmt, hints)
104 }
105 TableConnectError::NotBackupRoot => write!(
106 fmt,
107 "The path provided points to a database inside of an iOS backup, not the root of the backup."
108 ),
109 }
110 }
111}
112
113impl Display for TableError {
114 fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
115 match self {
116 TableError::CannotConnect(why) => write!(fmt, "{why}"),
117 TableError::CannotRead(why) => write!(fmt, "Cannot read from filesystem: {why}"),
118 TableError::QueryError(error) => write!(fmt, "Failed to query table: {error}"),
119 }
120 }
121}
122
123impl std::error::Error for TableError {
124 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
125 match self {
126 TableError::QueryError(e) => Some(e),
127 TableError::CannotConnect(e) => Some(e),
128 TableError::CannotRead(e) => Some(e),
129 }
130 }
131}
132
133impl std::error::Error for TableConnectError {
134 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
135 match self {
136 TableConnectError::Permissions(e) => Some(e),
137 TableConnectError::Inaccessible(_, e) => Some(e),
138 _ => None,
139 }
140 }
141}
142
143impl From<std::io::Error> for TableError {
144 fn from(err: std::io::Error) -> Self {
145 TableError::CannotRead(err)
146 }
147}
148
149impl From<rusqlite::Error> for TableError {
150 fn from(err: rusqlite::Error) -> Self {
151 TableError::QueryError(err)
152 }
153}