hexchat_api/user_data.rs
1use std::any::Any;
2use std::rc::Rc;
3use std::cell::RefCell;
4use std::sync::Arc;
5use std::sync::RwLock;
6
7/// Represents the user data that is provided to callbacks when they're invoked.
8/// A `UserData` object is registered with the callback using one of the
9/// hook commands. The user data can be virtually any type that implements the
10/// `Any` trait capable of being downcast to its original type. There are
11/// four variants for the user data. Which one to use depends on how the
12/// callback user data is shared. If the data is unique to one callback, then
13/// `BoxedData` should be enough. For single threaded sharing among more than
14/// one callback, `SharedData` will do the trick. If, for any odd sort of reason
15/// threading somehow becomes relevant to the user data object, `SyncData` would
16/// be the variant to use.
17///
18/// The class has 4 creation functions - one for each variant. And a convenience
19/// function that takes a closure that accepts the `UserData`'s wrapped value
20/// as a parameter. A good way to avoid all the dereferencing it takes to
21/// access the interior objects nested inside the sharing and mutability host
22/// objects.
23/// # Variants
24/// * `BoxedData` - Can hold user data that only one callback uses.
25/// * `SharedData` - Can allow more than one callback or other code to hold
26/// a cloned copy that references the same user data.
27/// * `SyncData` - Like `SharedData`, but uses the sync objects internally
28/// to allow the user data to be shared among threads.
29/// * `NoData` - Represents the absence of data.
30///
31#[derive(Debug)]
32pub enum UserData {
33 BoxedData ( Box < RefCell < dyn Any > > ),
34 SharedData ( Rc < RefCell < dyn Any > > ),
35 SyncData ( Arc < RwLock < dyn Any > > ),
36 NoData,
37}
38
39unsafe impl Send for UserData {}
40
41use UserData::*;
42
43use crate::HexchatError;
44
45impl UserData {
46 /// Creates a `BoxedData` variant. The type to use for user data that
47 /// isn't shared between Hexchat callbacks.
48 /// # Arguments
49 /// * `user_data` - The user data to box.
50 /// # Returns
51 /// * `BoxedData(user_data)`.
52 ///
53 pub fn boxed<D:'static>(user_data: D) -> Self {
54 BoxedData(Box::new(RefCell::new(user_data)))
55 }
56
57 /// Creates a `SharedData` variant instance. The type to use if the user
58 /// data needs to have shared access.
59 /// # Arguments
60 /// * `user_data` - The user data to wrap internally with `Rc<RefCell<_>>`.
61 /// # Returns
62 /// `SharedData(user_data)`.
63 ///
64 pub fn shared<D:'static>(user_data: D) -> Self {
65 SharedData(Rc::new(RefCell::new(user_data)))
66 }
67
68 /// Creates a `SyncData` variant. The type to use if the user data needs
69 /// to be accessible from other threads.
70 /// # Arguments
71 /// `user_data` - The user data to wrap internally with `Arc<Mutex<_>>`.
72 /// # Returns
73 /// * `SyncData(user_data)`.
74 ///
75 pub fn sync<D:'static>(user_data: D) -> Self {
76 SyncData(Arc::new(RwLock::new(user_data)))
77 }
78
79 /// Applies the given function to the wrapped object inside a `UserData`
80 /// object. The type of the wrapped data has to be compatible with the
81 /// type of the function's single parameter, or the downcast won't work
82 /// and `apply()` will panic.
83 /// # Arguments
84 /// * `f` - A function, or closure, to invoke with the user data, free of
85 /// any wrappers. The format of the function needs to be
86 /// `Fn(&T) -> R`, where `D` is the type of the user data; and `R`
87 /// is the return type that gets wrapped in an `Option` and returned
88 /// by `apply()`.
89 /// # Returns
90 /// * Returns the return value of function `f` if the downcast is
91 /// successful.
92 ///
93 pub fn apply<D:'static, F, R>(&self, f: F) -> R
94 where
95 F: FnOnce(&D) -> R
96 {
97 const ERRMSG: &str = "Unable to downcast to requested type.";
98 match self {
99 BoxedData(d) => {
100 f(d.borrow().downcast_ref::<D>().expect(ERRMSG))
101 },
102 SharedData(d) => {
103 f(d.borrow().downcast_ref::<D>().expect(ERRMSG))
104 },
105 SyncData(d) => {
106 f((*d.read().unwrap()).downcast_ref::<D>().expect(ERRMSG))
107 },
108 NoData => { panic!("Can't downcast `NoData`.") },
109 }
110 }
111
112 /// Same as the `apply()` function except allows mutable access to the
113 /// user data contents.
114 ///
115 pub fn apply_mut<D:'static, F, R>(&self, f: F) -> R
116 where
117 F: FnOnce(&mut D) -> R
118 {
119 const ERRMSG: &str = "Unable to downcast to requested type.";
120 match self {
121 BoxedData(d) => {
122 f(d.borrow_mut().downcast_mut::<D>().expect(ERRMSG))
123 },
124 SharedData(d) => {
125 f(d.borrow_mut().downcast_mut::<D>().expect(ERRMSG))
126 },
127 SyncData(d) => {
128 f((*d.write().unwrap()).downcast_mut::<D>().expect(ERRMSG))
129 },
130 NoData => { panic!("Can't downcast `NoData`.") },
131 }
132 }
133
134 /// Retrieves the user data from the `UserData` object. The type of the
135 /// user data must be the same as the type of the `UserData` object, or
136 /// the downcast will fail and return an error. The returned data is a
137 /// clone of the original value.
138 ///
139 /// # Generic Arguments
140 /// * `D` - The type of the user data to retrieve.
141 ///
142 /// # Returns
143 /// * `Ok(user_data)` - The user data if the downcast is successful.
144 /// * `Err(HexchatError::UserDataCastError)` - The error if the downcast
145 /// fails.
146 ///
147 pub fn get<D: 'static + Clone>(&self) -> Result<D, HexchatError> {
148 use HexchatError::UserDataCastError;
149 const ERRMSG: &str = "UserData::get() - Unable to downcast to \
150 requested type.";
151 match self {
152 BoxedData(d) => {
153 d.borrow()
154 .downcast_ref::<D>()
155 .cloned()
156 .ok_or_else(|| UserDataCastError(ERRMSG.into()))
157 },
158 SharedData(d) => {
159 d.borrow()
160 .downcast_ref::<D>()
161 .cloned()
162 .ok_or_else(|| UserDataCastError(ERRMSG.into()))
163 },
164 SyncData(d) => {
165 d.read().unwrap()
166 .downcast_ref::<D>()
167 .cloned()
168 .ok_or_else(|| UserDataCastError(ERRMSG.into()))
169 }
170 NoData => {
171 Err(UserDataCastError("`UserData::NoData` can't be cast."
172 .into()))
173 }
174 }
175 }
176
177 /// Sets the user data in the `UserData` object. The type of the user data
178 /// must be the same as the type of the `UserData` object, or the downcast
179 /// will fail and return an error.
180 ///
181 /// # Generic Arguments
182 /// * `D` - The type of the user data to set.
183 ///
184 /// # Arguments
185 /// * `value` - The value to set the user data to.
186 ///
187 /// # Returns
188 /// * `Ok(())` - The user data if the downcast is successful.
189 /// * `Err(HexchatError::UserDataCastError)` - The error if the downcast
190 /// fails.
191 ///
192 pub fn set<D: 'static>(&self, value: D) -> Result<(), HexchatError> {
193 use HexchatError::UserDataCastError;
194 const ERRMSG: &str = "`UserData::set()` - Unable to downcast to \
195 requested type.";
196 let mut success = false;
197 match self {
198 BoxedData(d) => {
199 if let Some(d) = d.borrow_mut().downcast_mut::<D>() {
200 *d = value;
201 success = true;
202 }
203 },
204 SharedData(d) => {
205 if let Some(d) = d.borrow_mut().downcast_mut::<D>() {
206 *d = value;
207 success = true;
208 }
209 },
210 SyncData(d) => {
211 if let Some(d) = d.write().unwrap().downcast_mut::<D>() {
212 *d = value;
213 success = true;
214 }
215 }
216 NoData => {
217 return Err(UserDataCastError("`UserData::NoData` can't be cast"
218 .into()));
219 }
220 }
221 if success {
222 Ok(())
223 } else {
224 Err(UserDataCastError(ERRMSG.into()))
225 }
226 }
227}
228
229impl Clone for UserData {
230 /// The clone operation for `UserData` allows each variant to be cloned,
231 /// except for `BoxedData`. The reason `BoxedData` is prohibited is to
232 /// deter sharing a box between callbacks, as that's not what
233 /// a box is meant to be used for. One of the shared variants is more
234 /// appropriate to share access to user data.
235 ///
236 fn clone(&self) -> Self {
237 match self {
238 SharedData(d) => { SharedData(d.clone()) },
239 SyncData(d) => { SyncData(d.clone()) },
240 NoData => { NoData },
241 BoxedData(_) => {
242 panic!("Can't clone `BoxedData`. If user data needs to be \
243 shared, The `SharedData` or `SyncData` variants of \
244 `UserData` should be used.")
245 },
246 }
247 }
248}
249
250impl Default for UserData {
251 /// Implemented to support the `take()` operation in `CallbackData` so the
252 /// user data can be retrieved with ownership when a callback is
253 /// deregistered. That take operation replaces the user data the callback
254 /// owns with the default value (`NoData`).
255 ///
256 fn default() -> Self { NoData }
257}
258
259