diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 111 |
1 files changed, 54 insertions, 57 deletions
diff --git a/src/lib.rs b/src/lib.rs index 51b4459..00d0845 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. +#![cfg_attr(feature="nightly_tests", feature(c_variadic))] +#![cfg_attr(feature="nightly_tests", feature(generic_associated_types))] +//#![deny(unsafe_op_in_unsafe_fn)] + //! Write hexchat plugins in Rust! //! //! This library provides safe API bindings for hexchat, but doesn't attempt to fix hexchat's own @@ -189,9 +193,6 @@ * -[ ] ??? */ -#![cfg_attr(feature="nightly_tests", feature(c_variadic))] -#![cfg_attr(feature="nightly_tests", feature(generic_associated_types))] - #[macro_use] extern crate impl_trait; #[doc(hidden)] @@ -574,41 +575,45 @@ impl_trait! { } } -impl<'ph, 'f> HookHandle<'ph, 'f> where 'f: 'ph { - /// If this is a timer hook, returns whether the hook has expired. - /// - /// Otherwise, returns false. - /// - /// # Examples - /// - /// ```no_run - /// use hexchat_unsafe_plugin::{HookHandle}; - /// - /// /// Remove timers that have expired. - /// fn clean_up_timers(timers: &mut Vec<HookHandle<'_, '_>>) { - /// timers.retain(|timer| { - /// !timer.expired() - /// }); - /// } - /// ``` - pub fn expired(&self) -> bool { - self.freed.get() - } -} - -impl<'ph, 'f> Drop for HookHandle<'ph, 'f> where 'f: 'ph { - fn drop(&mut self) { - if self.freed.get() { - // already free'd. - return; - } - self.freed.set(true); - unsafe { - let b = ((*self.ph.as_raw()).hexchat_unhook)(self.ph, self.hh) as *mut HookUd<'f>; - // we assume b is not null. this should be safe. - // just drop it - drop(Rc::from_raw(b)); +impl_trait! { + impl<'ph, 'f> HookHandle<'ph, 'f> where 'f: 'ph { + /// If this is a timer hook, returns whether the hook has expired. + /// + /// Otherwise, returns false. + /// + /// # Examples + /// + /// ```no_run + /// use hexchat_unsafe_plugin::{HookHandle}; + /// + /// /// Remove timers that have expired. + /// fn clean_up_timers(timers: &mut Vec<HookHandle<'_, '_>>) { + /// timers.retain(|timer| { + /// !timer.expired() + /// }); + /// } + /// ``` + pub fn expired(&self) -> bool { + self.freed.get() + } + + impl trait Drop { + fn drop(&mut self) { + if self.freed.get() { + // already free'd. + return; + } + self.freed.set(true); + unsafe { + let b = ((*self.ph.as_raw()).hexchat_unhook)(self.ph, self.hh) as *mut HookUd<'f>; + // we assume b is not null. this should be safe. + // just drop it + drop(Rc::from_raw(b)); + } + } } + + impl trait RefUnwindSafe {} } } @@ -676,7 +681,7 @@ unsafe fn call_hook_protected<'ph, F>( ph: MutLtPtr<'ph, RawPh<'ph>>, f: F ) -> Eat -where F: FnOnce() -> Eat + UnwindSafe{ +where F: FnOnce() -> Eat + UnwindSafe { match catch_unwind(f) { Result::Ok(v @ _) => v, Result::Err(e @ _) => { @@ -919,7 +924,7 @@ impl<'ph> PluginHandle<'ph> { /// ] /// } /// ``` - pub fn hook_command<'f, F>(&self, cmd: &str, pri: i32, help: Option<&str>, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, WordEol) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_command<'f, F>(&self, cmd: &str, pri: i32, help: Option<&str>, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>, WordEol<'_>) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); unsafe extern "C" fn callback(word: *const *const c_char, word_eol: *const *const c_char, ud: *mut c_void) -> c_int { let f: Rc<HookUd> = rc_clone_from_raw(ud as *const HookUd); @@ -970,12 +975,12 @@ impl<'ph> PluginHandle<'ph> { /// ] /// } /// ``` - pub fn hook_server<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, WordEol) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_server<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>, WordEol<'_>) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); self.hook_server_attrs(cmd, pri, move |ph, w, we, _| cb(ph, w, we)) } /// Sets a server hook, with attributes. - pub fn hook_server_attrs<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, WordEol, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_server_attrs<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>, WordEol<'_>, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); unsafe extern "C" fn callback(word: *const *const c_char, word_eol: *const *const c_char, attrs: *const RawAttrs, ud: *mut c_void) -> c_int { let f: Rc<HookUd> = rc_clone_from_raw(ud as *const HookUd); @@ -1028,7 +1033,7 @@ impl<'ph> PluginHandle<'ph> { /// ] /// } /// ``` - pub fn hook_print<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_print<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); // hmm, is there any way to avoid this code duplication? // hook_print is special because dummy prints (keypresses, Close Context) are handled @@ -1092,7 +1097,7 @@ impl<'ph> PluginHandle<'ph> { /// ] /// } /// ``` - pub fn hook_print_attrs<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_print_attrs<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { assert!(self.guiplugin.is_null(), "PluginEntryHandle can't have hooks"); unsafe extern "C" fn callback(word: *const *const c_char, attrs: *const RawAttrs, ud: *mut c_void) -> c_int { let f: Rc<HookUd> = rc_clone_from_raw(ud as *const HookUd); @@ -1938,31 +1943,31 @@ impl<'a, 'ph: 'a> ValidContext<'a, 'ph> { /// Sets a command hook. /// /// See [`PluginHandle::hook_command`]. - pub fn hook_command<'f, F>(&self, cmd: &str, pri: i32, help: Option<&str>, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, WordEol) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_command<'f, F>(&self, cmd: &str, pri: i32, help: Option<&str>, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>, WordEol<'_>) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { self.ph.hook_command(cmd, pri, help, cb) } /// Sets a server hook. /// /// See [`PluginHandle::hook_server`]. - pub fn hook_server<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, WordEol) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_server<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>, WordEol<'_>) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { self.ph.hook_server(cmd, pri, cb) } /// Sets a server hook with attributes. /// /// See [`PluginHandle::hook_server_attrs`]. - pub fn hook_server_attrs<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, WordEol, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_server_attrs<'f, F>(&self, cmd: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>, WordEol<'_>, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { self.ph.hook_server_attrs(cmd, pri, cb) } /// Sets a print hook. /// /// See [`PluginHandle::hook_print`]. - pub fn hook_print<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_print<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { self.ph.hook_print(name, pri, cb) } /// Sets a print hook with attributes. /// /// See [`PluginHandle::hook_print_attrs`]. - pub fn hook_print_attrs<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { + pub fn hook_print_attrs<'f, F>(&self, name: &str, pri: i32, cb: F) -> HookHandle<'ph, 'f> where F: Fn(&mut PluginHandle<'ph>, Word<'_>, EventAttrs) -> Eat + 'f + RefUnwindSafe, 'f: 'ph { self.ph.hook_print_attrs(name, pri, cb) } /// Sets a timer hook. @@ -2027,20 +2032,12 @@ impl<'a, 'ph: 'a> ValidContext<'a, 'ph> { // ******* // // Type aliases, used for safety type checking. -// /// Userdata type used by a command hook. +/// Userdata type used by a hook // We actually do want RefUnwindSafe. This function may be called multiple times, and it's not // automatically invalidated if it panics, so it may be called again after it panics. If you need // mutable state, std provides std::sync::Mutex which has poisoning. Other interior mutability with // poisoning could also be used. std doesn't have anything for single-threaded performance (yet), // but hexchat isn't particularly performance-critical. -// type CommandHookUd = Box<dyn Fn(Word, WordEol) -> Eat + std::panic::RefUnwindSafe>; -// /// Userdata type used by a server hook. -// type ServerHookUd = Box<dyn Fn(Word, WordEol, EventAttrs) -> Eat + std::panic::RefUnwindSafe>; -// /// Userdata type used by a print hook. -// type PrintHookUd = Box<dyn Fn(Word, EventAttrs) -> Eat + std::panic::RefUnwindSafe>; -// /// Userdata type used by a timer hook. -// type TimerHookUd = Box<dyn Fn() -> bool + std::panic::RefUnwindSafe>; -/// Userdata type used by a hook type HookUd<'f> = Box<dyn Fn(*const *const c_char, *const *const c_char, *const RawAttrs) -> Eat + RefUnwindSafe + 'f>; /// Contexts type Contexts = Rc<AssertUnwindSafe<RefCell<HashSet<Rc<*const internals::HexchatContext>>>>>; |