1#![forbid(unsafe_code)] use std::sync::Arc;
6
7use crate::FsMistrustErrorExt as _;
8use crate::slug::BadSlug;
9use fs_mistrust::anon_home::PathExt as _;
10use tor_basic_utils::PathExt as _;
11use tor_error::{Bug, ErrorKind, into_bad_api_usage};
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("constructing storage manager")]
74 Initializing,
75 #[display("enumerating instances")]
77 Enumerating,
78}
79
80#[derive(thiserror::Error, Debug, Clone)]
85#[non_exhaustive]
86pub enum ErrorSource {
87 #[error("IO error")]
89 IoError(#[source] Arc<std::io::Error>),
90
91 #[error("Problem accessing persistent state")]
93 Inaccessible(#[source] fs_mistrust::Error),
94
95 #[deprecated = "use ErrorSource::Inaccessible instead"]
100 #[error("Problem accessing persistent state")]
101 Permissions(#[source] fs_mistrust::Error),
102
103 #[error("Storage not locked")]
109 NoLock,
110
111 #[error("JSON error")]
113 Serde(#[from] Arc<serde_json::Error>),
114
115 #[error("State already lockedr")]
117 AlreadyLocked,
118
119 #[error("Programming error")]
121 Bug(#[from] Bug),
122}
123
124impl From<BadSlug> for ErrorSource {
125 fn from(bs: BadSlug) -> ErrorSource {
126 into_bad_api_usage!("bad slug")(bs).into()
127 }
128}
129impl From<BadSlug> for Error {
134 fn from(bs: BadSlug) -> Error {
135 Error::new(bs, Action::Initializing, Resource::Manager)
138 }
139}
140
141#[derive(Clone, Debug, derive_more::Display)]
143#[display("{} while {} on {}", source, action, resource)]
144pub struct Error {
145 source: ErrorSource,
147 action: Action,
149 resource: Resource,
151}
152
153impl std::error::Error for Error {
154 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
155 self.source.source()
156 }
157}
158
159impl Error {
160 pub fn source(&self) -> &ErrorSource {
162 &self.source
163 }
164
165 pub(crate) fn new(err: impl Into<ErrorSource>, action: Action, resource: Resource) -> Self {
167 Error {
168 source: err.into(),
169 action,
170 resource,
171 }
172 }
173}
174
175impl tor_error::HasKind for Error {
176 #[rustfmt::skip] fn kind(&self) -> ErrorKind {
178 use ErrorSource as E;
179 use tor_error::ErrorKind as K;
180 #[allow(deprecated)]
181 match &self.source {
182 E::IoError(..) => K::PersistentStateAccessFailed,
183 E::Permissions(e) => e.state_error_kind(),
184 E::Inaccessible(e) => e.state_error_kind(),
185 E::NoLock => K::BadApiUsage,
186 E::AlreadyLocked => K::LocalResourceAlreadyInUse,
187 E::Bug(e) => e.kind(),
188 E::Serde(..) if self.action == Action::Storing => K::Internal,
189 E::Serde(..) => K::PersistentStateCorrupted,
190 }
191 }
192}
193
194impl From<std::io::Error> for ErrorSource {
195 fn from(e: std::io::Error) -> ErrorSource {
196 ErrorSource::IoError(Arc::new(e))
197 }
198}
199
200impl From<serde_json::Error> for ErrorSource {
201 fn from(e: serde_json::Error) -> ErrorSource {
202 ErrorSource::Serde(Arc::new(e))
203 }
204}
205
206impl From<fs_mistrust::Error> for ErrorSource {
207 fn from(e: fs_mistrust::Error) -> ErrorSource {
208 match e {
209 fs_mistrust::Error::Io { err, .. } => ErrorSource::IoError(err),
210 other => ErrorSource::Inaccessible(other),
211 }
212 }
213}
214
215#[cfg(all(test, not(miri) ))]
216mod test {
217 #![allow(clippy::bool_assert_comparison)]
219 #![allow(clippy::clone_on_copy)]
220 #![allow(clippy::dbg_macro)]
221 #![allow(clippy::mixed_attributes_style)]
222 #![allow(clippy::print_stderr)]
223 #![allow(clippy::print_stdout)]
224 #![allow(clippy::single_char_pattern)]
225 #![allow(clippy::unwrap_used)]
226 #![allow(clippy::unchecked_time_subtraction)]
227 #![allow(clippy::useless_vec)]
228 #![allow(clippy::needless_pass_by_value)]
229 #![allow(clippy::string_slice)] use super::*;
233 use std::io;
234 use tor_error::ErrorReport as _;
235
236 #[test]
237 fn error_display() {
238 assert_eq!(
239 Error::new(
240 io::Error::from(io::ErrorKind::PermissionDenied),
241 Action::Initializing,
242 Resource::InstanceState {
243 state_dir: "/STATE_DIR".into(),
244 kind: "KIND".into(),
245 identity: "IDENTY".into(),
246 }
247 )
248 .report()
249 .to_string(),
250 r#"error: IO error while constructing storage manager on instance "KIND"/"IDENTY" in /STATE_DIR: permission denied"#
251 );
252 }
253}