1use crate::*;
2use std::error::Error;
3use std::path::PathBuf;
4
5#[derive(Debug)]
7pub enum ConfigError {
8 ConfigNotFound(String),
10 ConfigRecursiveNotFound(String),
12 ConfigTypeMismatch(String, &'static str, &'static str),
14 ConfigParseError(String, String),
16 ConfigRecursiveError(String),
18 ConfigFileNotExists(PathBuf),
20 ConfigFileNotSupported(PathBuf),
22 RefValueRecursiveError,
24 TooManyInstances(usize),
26 LockPoisoned,
28 ConfigCause(Box<dyn Error + 'static>),
30}
31
32impl<E: Error + 'static> From<E> for ConfigError {
33 #[inline]
34 fn from(e: E) -> Self {
35 ConfigError::ConfigCause(Box::new(e))
36 }
37}
38
39impl ConfigError {
40 #[inline]
41 pub(crate) fn try_lock_err<T>(v: TryLockError<T>) -> Option<Self> {
42 match v {
43 TryLockError::WouldBlock => None,
44 TryLockError::Poisoned(e) => Some(Self::lock_err(e)),
45 }
46 }
47
48 #[inline]
49 pub(crate) fn lock_err<T>(_e: PoisonError<T>) -> Self {
50 ConfigError::LockPoisoned
51 }
52}
53
54pub(crate) trait ConfigLock<'a, T> {
55 fn lock_c(&'a self) -> Result<MutexGuard<'a, T>, ConfigError>;
56
57 fn try_lock_c(&'a self) -> Result<Option<MutexGuard<'a, T>>, ConfigError>;
58}
59
60impl<'a, T> ConfigLock<'a, T> for Mutex<T> {
61 #[inline]
62 fn lock_c(&'a self) -> Result<MutexGuard<'a, T>, ConfigError> {
63 self.lock().map_err(ConfigError::lock_err)
64 }
65
66 #[inline]
67 fn try_lock_c(&'a self) -> Result<Option<MutexGuard<'a, T>>, ConfigError> {
68 let v = self.try_lock().map_err(ConfigError::try_lock_err);
69 match v {
70 Ok(ok) => Ok(Some(ok)),
71 Err(Some(e)) => Err(e),
72 _ => Ok(None),
73 }
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use std::sync::{Arc, Mutex};
81 use std::thread;
82 use std::time::Duration;
83
84 #[test]
85 fn config_error_from_converts_to_configcause() {
86 let io_err = std::io::Error::new(std::io::ErrorKind::Other, "io");
87 let ce: ConfigError = io_err.into();
88 match ce {
89 ConfigError::ConfigCause(_) => {}
90 _ => panic!("Expected ConfigCause variant"),
91 }
92 }
93
94 #[test]
95 fn try_lock_err_variants_and_poison_detection() {
96 assert!(ConfigError::try_lock_err(TryLockError::WouldBlock::<()>).is_none());
98
99 let m = Arc::new(Mutex::new(()));
101 let mm = m.clone();
102 let h = thread::spawn(move || {
103 let _g = mm.lock().unwrap();
104 panic!("poison");
105 });
106 let _ = h.join();
108
109 {
111 let try_result = m.try_lock();
112 match try_result {
113 Err(e) => {
114 let opt = ConfigError::try_lock_err(e);
116 assert!(opt.is_some());
117 if let Some(err) = opt {
118 match err {
119 ConfigError::LockPoisoned => {}
120 _ => panic!("Expected LockPoisoned"),
121 }
122 }
123 }
124 Ok(_) => panic!("Expected poisoned mutex"),
125 }
126 }
127 }
128
129 #[test]
130 fn configlock_mutex_lock_c_and_try_lock_c_behaviour() {
131 let m_ok = Mutex::new(1);
133 assert!(m_ok.lock_c().is_ok());
134
135 let m_block = Arc::new(Mutex::new(0));
137 let m_block_c = m_block.clone();
138 let handle = thread::spawn(move || {
139 let _g = m_block_c.lock().unwrap();
140 thread::sleep(Duration::from_millis(200));
141 });
143 thread::sleep(Duration::from_millis(10));
145 match m_block.try_lock_c().unwrap() {
146 None => {} Some(_) => panic!("Expected None when mutex is held by another thread"),
148 }
149 handle.join().unwrap();
150
151 let m_poison = Arc::new(Mutex::new(()));
153 let mm = m_poison.clone();
154 let h2 = thread::spawn(move || {
155 let _g = mm.lock().unwrap();
156 panic!("poison");
157 });
158 let _ = h2.join();
159
160 assert!(matches!(
162 m_poison.try_lock_c(),
163 Err(ConfigError::LockPoisoned)
164 ));
165
166 assert!(matches!(m_poison.lock_c(), Err(ConfigError::LockPoisoned)));
168 }
169}