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