summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2022-04-26 09:14:09 -0300
committerSoniEx2 <endermoneymod@gmail.com>2022-04-26 09:14:09 -0300
commit25d5d6f4e9ed901d284633691941c52c8556d17c (patch)
treec353158113eba06e67e37a77caf337a8bf1c270e
parent83db3d819b7402b4f3c6ca023ddfea6204fae4b4 (diff)
Project ValidContext to Fields<'_, '_, Contexts>
-rw-r--r--src/extra_tests.rs20
-rw-r--r--src/lib.rs63
-rw-r--r--src/list.rs18
3 files changed, 76 insertions, 25 deletions
diff --git a/src/extra_tests.rs b/src/extra_tests.rs
index 8aed5a9..b64cc22 100644
--- a/src/extra_tests.rs
+++ b/src/extra_tests.rs
@@ -4,7 +4,7 @@
 //! use std::mem;
 //! use hexchat_unsafe_plugin::PluginHandle;
 //!
-//! fn prepoop_your_pants_pluginhandle<'ph>(ph: &mut PluginHandle<'ph>) {
+//! fn prepoop_your_pants_pluginhandle(ph: &mut PluginHandle<'_>) {
 //!     let temporary: String = "Hello, world!".to_string();
 //!     let hook = ph.hook_timer(0, |_| {
 //!         println!("{}", temporary);
@@ -12,3 +12,21 @@
 //!     });
 //! }
 //! ```
+//!
+//! ```compile_fail
+//! use hexchat_unsafe_plugin::list::Contexts;
+//! use hexchat_unsafe_plugin::PluginHandle;
+//!
+//! fn unsound(ph: &mut PluginHandle<'_>) {
+//!     ph.ensure_valid_context(|mut ph| {
+//!         let mut list = ph.list(&Contexts);
+//!         let context = list.next().unwrap();
+//!         write!(ph, "{}", context.name().unwrap());
+//!         std::mem::forget(list);
+//!         write!(ph, "{}", context.name().unwrap());
+//!         let ctx = context.context();
+//!         ph.set_context(&ctx);
+//!         write!(ph, "{}", context.name().unwrap());
+//!     })
+//! }
+//! ```
diff --git a/src/lib.rs b/src/lib.rs
index f0ee389..52d3d23 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -150,7 +150,7 @@
  *         -[ ] win_ptr
  *     -[x] hexchat_get_prefs
  *     -[x] hexchat_list_get
- *     -[ ] hexchat_list_fields
+ *     -[x] ~~hexchat_list_fields~~ not available. no alternative provided.
  *     -[x] hexchat_list_next
  *     -[x] hexchat_list_str
  *     -[x] hexchat_list_int
@@ -338,16 +338,21 @@ pub enum PrefValue {
 }
 
 mod valid_context {
+    use std::ptr;
+
+    use crate::list;
     use crate::PluginHandle;
 
-    /// A PluginHandle operating on a valid context.
+    /// A `PluginHandle` operating on a valid context.
     ///
-    /// This mechanism attempts to reduce the likelihood of segfaults in
-    /// hexchat code.
+    /// Methods that require a valid plugin context are only available through
+    /// this. Methods that may invalidate the plugin context also consume this.
     ///
     /// See also [`PluginHandle::ensure_valid_context`].
     pub struct ValidContext<'a, 'ph: 'a> {
-        pub(crate) ph: &'a mut PluginHandle<'ph>,
+        // NOTE: NOT mut(!)
+        pub(crate) ph: &'a PluginHandle<'ph>,
+        fields: list::Fields<'a, 'ph, list::Contexts>,
         _hidden: (),
     }
 
@@ -358,7 +363,29 @@ mod valid_context {
         ///
         /// The PluginHandle's context must be valid.
         pub(crate) unsafe fn new(ph: &'a mut PluginHandle<'ph>) -> Self {
-            Self { ph, _hidden: () }
+            static CONTEXTS: list::Contexts = list::Contexts;
+            Self {
+                ph: &*ph,
+                fields: list::Fields {
+                    context: &*ph,
+                    // this isn't actually documented but hexchat explicitly
+                    // supports passing in NULL for the current context.
+                    // this gives access to some properties that aren't
+                    // available through get_info.
+                    list: ptr::null_mut(),
+                    _t: &CONTEXTS,
+                    id: 0,
+                    valid: Default::default(),
+                },
+                _hidden: (),
+            }
+        }
+    }
+
+    impl<'a, 'ph: 'a> std::ops::Deref for ValidContext<'a, 'ph> {
+        type Target = list::Fields<'a, 'ph, list::Contexts>;
+        fn deref(&self) -> &Self::Target {
+            &self.fields
         }
     }
 }
@@ -749,13 +776,8 @@ impl<'ph> PluginHandle<'ph> {
     ///
     /// Returns `true` if the context is valid, `false` otherwise.
     pub fn set_context(&mut self, ctx: &Context<'ph>) -> bool {
-        if let Some(ctx) = ctx.ctx.upgrade() {
-            unsafe {
-                ph_call!(hexchat_set_context(self, *ctx)) != 0
-            }
-        } else {
-            false
-        }
+        // we could've made this &self but we didn't. avoid breaking API.
+        self.set_context_internal(ctx)
     }
 
     /// Do something in a valid context.
@@ -1278,6 +1300,17 @@ impl<'ph> PluginHandle<'ph> {
             ctx.map(|ctx| wrap_context(self, ctx))
         }
     }
+
+    /// Same as `set_context` but it takes `&self`.
+    fn set_context_internal(&self, ctx: &Context<'ph>) -> bool {
+        if let Some(ctx) = ctx.ctx.upgrade() {
+            unsafe {
+                ph_call!(hexchat_set_context(self, *ctx)) != 0
+            }
+        } else {
+            false
+        }
+    }
 }
 
 impl<'a> EventAttrs<'a> {
@@ -1544,7 +1577,7 @@ impl<'a, 'ph: 'a> ValidContext<'a, 'ph> {
             ph_call!(hexchat_list_get(ph, list.as_ptr()))
         };
         list::Entries {
-            context: self,
+            context: self.ph,
             list: list,
             t: t,
             valid: Default::default(),
@@ -1575,7 +1608,7 @@ impl<'a, 'ph: 'a> ValidContext<'a, 'ph> {
     // context.
     // So either way we're still in a valid context when this returns.
     pub fn set_context(&mut self, ctx: &Context<'ph>) -> bool {
-        self.ph.set_context(ctx)
+        self.ph.set_context_internal(ctx)
     }
 
     /// Prints to the hexchat buffer.
diff --git a/src/list.rs b/src/list.rs
index 04af87a..891fdd6 100644
--- a/src/list.rs
+++ b/src/list.rs
@@ -53,7 +53,7 @@ pub struct Users;
 
 /// Entries.
 pub struct Entries<'a, 'ph, T> {
-    pub(crate) context: &'a crate::ValidContext<'a, 'ph>,
+    pub(crate) context: &'a crate::PluginHandle<'ph>,
     pub(crate) list: *mut crate::internals::HexchatList,
     pub(crate) t: &'a T,
     pub(crate) valid: Rc<Cell<usize>>,
@@ -61,11 +61,11 @@ pub struct Entries<'a, 'ph, T> {
 
 /// Fields.
 pub struct Fields<'a, 'ph, T> {
-    context: &'a crate::ValidContext<'a, 'ph>,
-    list: *mut crate::internals::HexchatList,
-    _t: &'a T,
-    id: usize,
-    valid: Rc<Cell<usize>>,
+    pub context: &'a crate::PluginHandle<'ph>,
+    pub list: *mut crate::internals::HexchatList,
+    pub _t: &'a T,
+    pub id: usize,
+    pub valid: Rc<Cell<usize>>,
 }
 
 // impls
@@ -98,7 +98,7 @@ impl List for Users {
 
 impl<'a, 'ph, T> Drop for Entries<'a, 'ph, T> {
     fn drop(&mut self) {
-        let ph = &self.context.ph;
+        let ph = &self.context;
         self.valid.set(usize::MAX);
         unsafe {
             ph_call!(hexchat_list_free(ph, self.list));
@@ -110,7 +110,7 @@ impl<'a, 'ph, T> Iterator for Entries<'a, 'ph, T> {
     type Item = Fields<'a, 'ph, T>;
 
     fn next(&mut self) -> Option<Self::Item> {
-        let ph = &self.context.ph;
+        let ph = &self.context;
         let id = self.valid.get();
         if unsafe {
             ph_call!(hexchat_list_next(ph, self.list))
@@ -138,7 +138,7 @@ macro_rules! field {
                 self.id, self.valid.get(),
                 "instances of Fields are invalidated by next()",
             );
-            let $ph = &self.context.ph;
+            let $ph = &self.context;
             const NAME: &'static CStr = unsafe {
                 CStr::from_bytes_with_nul_unchecked(
                     $n.as_bytes()