1#![forbid(unsafe_code)] use std::sync::Arc;
6
7use crate::slug::BadSlug;
8use crate::FsMistrustErrorExt as _;
9use fs_mistrust::anon_home::PathExt as _;
10use tor_basic_utils::PathExt as _;
11use tor_error::{into_bad_api_usage, Bug, ErrorKind};
12
13#[derive(Debug, Clone, derive_more::Display)]
15pub(crate) enum Resource {
16 #[display("persistent storage manager")]
18 Manager,
19 #[display("directory {}", dir.anonymize_home())]
21 Directory {
22 dir: std::path::PathBuf,
24 },
25 #[display("{} in {}", file.display_lossy(), container.anonymize_home())]
27 File {
28 container: std::path::PathBuf,
30 file: std::path::PathBuf,
32 },
33 #[cfg(feature = "testing")]
35 #[display("{} in memory-backed store", key)]
36 Temporary {
37 key: String,
39 },
40 #[display(
42 "instance {:?}/{:?} in {}",
43 kind,
44 identity,
45 state_dir.anonymize_home()
46 )]
47 InstanceState {
48 state_dir: std::path::PathBuf,
50 kind: String,
52 identity: String,
54 },
55}
56
57#[derive(Debug, Clone, derive_more::Display, Eq, PartialEq)]
59pub(crate) enum Action {
60 #[display("loading persistent data")]
62 Loading,
63 #[display("storing persistent data")]
65 Storing,
66 #[display("deleting persistent data")]
68 Deleting,
69 #[display("acquiring lock")]
71 Locking,
72 #[display("releasing lock")]
74 Unlocking,
75 #[display("constructing storage manager")]
77 Initializing,
78 #[display("enumerating instances")]
80 Enumerating,
81}
82
83#[derive(thiserror::Error, Debug, Clone)]
88#[non_exhaustive]
89pub enum ErrorSource {
90 #[error("IO error")]
92 IoError(#[source] Arc<std::io::Error>),
93
94 #[error("Problem accessing persistent state")]
96 Inaccessible(#[source] fs_mistrust::Error),
97
98 #[deprecated = "use ErrorSource::Inaccessible instead"]
103 #[error("Problem accessing persistent state")]
104 Permissions(#[source] fs_mistrust::Error),
105
106 #[error("Storage not locked")]
112 NoLock,
113
114 #[error("JSON error")]
116 Serde(#[from] Arc<serde_json::Error>),
117
118 #[error("State already lockedr")]
120 AlreadyLocked,
121
122 #[error("Programming error")]
124 Bug(#[from] Bug),
125}
126
127impl From<BadSlug> for ErrorSource {
128 fn from(bs: BadSlug) -> ErrorSource {
129 into_bad_api_usage!("bad slug")(bs).into()
130 }
131}
132impl From<BadSlug> for Error {
137 fn from(bs: BadSlug) -> Error {
138 Error::new(bs, Action::Initializing, Resource::Manager)
141 }
142}
143
144#[derive(Clone, Debug, derive_more::Display)]
146#[display("{} while {} on {}", source, action, resource)]
147pub struct Error {
148 source: ErrorSource,
150 action: Action,
152 resource: Resource,
154}
155
156impl std::error::Error for Error {
157 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
158 self.source.source()
159 }
160}
161
162impl Error {
163 pub fn source(&self) -> &ErrorSource {
165 &self.source
166 }
167
168 pub(crate) fn new(err: impl Into<ErrorSource>, action: Action, resource: Resource) -> Self {
170 Error {
171 source: err.into(),
172 action,
173 resource,
174 }
175 }
176}
177
178impl tor_error::HasKind for Error {
179 #[rustfmt::skip] fn kind(&self) -> ErrorKind {
181 use ErrorSource as E;
182 use tor_error::ErrorKind as K;
183 #[allow(deprecated)]
184 match &self.source {
185 E::IoError(..) => K::PersistentStateAccessFailed,
186 E::Permissions(e) => e.state_error_kind(),
187 E::Inaccessible(e) => e.state_error_kind(),
188 E::NoLock => K::BadApiUsage,
189 E::AlreadyLocked => K::LocalResourceAlreadyInUse,
190 E::Bug(e) => e.kind(),
191 E::Serde(..) if self.action == Action::Storing => K::Internal,
192 E::Serde(..) => K::PersistentStateCorrupted,
193 }
194 }
195}
196
197impl From<std::io::Error> for ErrorSource {
198 fn from(e: std::io::Error) -> ErrorSource {
199 ErrorSource::IoError(Arc::new(e))
200 }
201}
202
203impl From<serde_json::Error> for ErrorSource {
204 fn from(e: serde_json::Error) -> ErrorSource {
205 ErrorSource::Serde(Arc::new(e))
206 }
207}
208
209impl From<fs_mistrust::Error> for ErrorSource {
210 fn from(e: fs_mistrust::Error) -> ErrorSource {
211 match e {
212 fs_mistrust::Error::Io { err, .. } => ErrorSource::IoError(err),
213 other => ErrorSource::Inaccessible(other),
214 }
215 }
216}
217
218#[cfg(all(test, not(miri) ))]
219mod test {
220 #![allow(clippy::bool_assert_comparison)]
222 #![allow(clippy::clone_on_copy)]
223 #![allow(clippy::dbg_macro)]
224 #![allow(clippy::mixed_attributes_style)]
225 #![allow(clippy::print_stderr)]
226 #![allow(clippy::print_stdout)]
227 #![allow(clippy::single_char_pattern)]
228 #![allow(clippy::unwrap_used)]
229 #![allow(clippy::unchecked_duration_subtraction)]
230 #![allow(clippy::useless_vec)]
231 #![allow(clippy::needless_pass_by_value)]
232 use super::*;
235 use std::io;
236 use tor_error::ErrorReport as _;
237
238 #[test]
239 fn error_display() {
240 assert_eq!(
241 Error::new(
242 io::Error::from(io::ErrorKind::PermissionDenied),
243 Action::Initializing,
244 Resource::InstanceState {
245 state_dir: "/STATE_DIR".into(),
246 kind: "KIND".into(),
247 identity: "IDENTY".into(),
248 }
249 )
250 .report()
251 .to_string(),
252 r#"error: IO error while constructing storage manager on instance "KIND"/"IDENTY" in /STATE_DIR: permission denied"#
253 );
254 }
255}