summary refs log tree commit diff stats
path: root/src/mock/mod.rs
diff options
context:
space:
mode:
authorSoniEx2 <endermoneymod@gmail.com>2022-06-20 00:19:56 -0300
committerSoniEx2 <endermoneymod@gmail.com>2022-06-20 00:19:56 -0300
commit342d0b3ba53512c98cc5540313f874fe543d4b9b (patch)
tree4de8305590a3ebd80f003f5fb4ed7938b6168e4e /src/mock/mod.rs
parent890d8bbef986d8b85d6eaaddd4c8895c5f61c09c (diff)
Begin the great refactoring
Diffstat (limited to 'src/mock/mod.rs')
-rw-r--r--src/mock/mod.rs465
1 files changed, 298 insertions, 167 deletions
diff --git a/src/mock/mod.rs b/src/mock/mod.rs
index 92de66c..0e4ba03 100644
--- a/src/mock/mod.rs
+++ b/src/mock/mod.rs
@@ -38,8 +38,11 @@
 //!
 //! This API is not stable. Do not rely on this API to be stable.
 
+use crate::internals::Ph;
+
 use std::cell::Cell;
 use std::cell::RefCell;
+use std::cell::UnsafeCell;
 use std::ffi::CStr;
 use std::ffi::CString;
 use std::marker::PhantomData;
@@ -48,24 +51,40 @@ use std::pin::Pin;
 use std::ptr;
 use std::rc::Rc;
 
-use libc::c_char;
+use ::libc::{c_char, c_int};
 
 use ::ltptr::*;
 
-use ::selfref::{Holder, Opaque, SelfRef, opaque};
+use ::selfref::{Holder, NewWith, OperateIn, opaque};
+
+struct Context {
+}
 
-// MUST be repr(C)
+// MUST be repr(C), despite having repr(Rust) types inside it.
+/// A loaded plugin/complete plugin handle, including private fields.
 #[repr(C)]
-struct MockPlugin<'dangling, 'env> {
+struct MockPlugin<'ph, 'env: 'ph> {
     // NOTE: MUST be first thing in the struct.
-    methods: crate::internals::Ph,
-    filename: *const c_char,
-    plugin_name: Cell<*const c_char>,
-    plugin_desc: Cell<*const c_char>,
-    plugin_vers: Cell<*const c_char>,
+    // we actually pass the whole MockPlugin to the plugin, but we pretend it's
+    // just the Ph.
+    methods: Ph<'ph>,
+    filename: CString,
+    plugin_name: *const c_char,
+    plugin_desc: *const c_char,
+    plugin_vers: *const c_char,
     free_strings: bool,
-    env: &'env PluginEnvironment<'dangling, 'env>,
-    _pin: PhantomPinned,
+    env: Pin<&'env PluginEnvironment<'env>>,
+    deinit: Option<unsafe extern "C" fn(ph: MutLtPtr<'ph, Ph<'ph>>) -> c_int>,
+    context: Option<Rc<Context>>,
+}
+
+struct MockPluginK<'env> {
+    _p: PhantomData<&'env PluginEnvironment<'env>>,
+}
+opaque! {
+    impl['env] Opaque for MockPluginK<'env> {
+        type Kind<'ph> = UnsafeCell<MockPlugin<'ph, 'env>>;
+    }
 }
 
 enum MockCallback {
@@ -96,9 +115,9 @@ enum MockCallback {
     HookDeleted,
 }
 
-struct MockHook<'dangling, 'env> {
-    //pl: Rc<Holder<'dangling, MockPluginK<'dangling, 'env>>>,
-    pl: Rc<MockPlugin<'dangling, 'env>>,
+struct MockHook<'env> {
+    pl: Rc<Holder<'env, MockPluginK<'env>>>,
+    //pl: Rc<MockPlugin<'env>>,
     cb: RefCell<MockCallback>,
     name: Option<Box<str>>,
     pri_or_fd: libc::c_int,
@@ -106,32 +125,39 @@ struct MockHook<'dangling, 'env> {
     _pin: PhantomPinned,
 }
 
-pub struct PluginEnvironment<'dangling, 'env> {
-    //plugins: RefCell<Vec<Pin<Rc<Holder<'dangling, MockPluginK<'dangling, 'env>>>>>>,
-    plugins: RefCell<Vec<Pin<Rc<MockPlugin<'dangling, 'env>>>>>,
-    // priority-based hooks
-    //hooks: RefCell<Vec<Pin<Rc<Holder<'dangling, MockHookK<'dangling, 'env>>>>>>,
-    hooks: RefCell<Vec<Pin<Rc<MockHook<'dangling, 'env>>>>>,
-    // fd/timer hooks
-    //extra_hooks: RefCell<Vec<Pin<Rc<Holder<'dangling, MockHookK<'dangling, 'env>>>>>>,
-    extra_hooks: RefCell<Vec<Pin<Rc<MockHook<'dangling, 'env>>>>>,
-    depth: Cell<usize>,
+struct MockHookK<'env> {
+    _p: PhantomData<&'env PluginEnvironment<'env>>,
+}
+opaque! {
+    impl['env] Opaque for MockHookK<'env> {
+        type Kind<'ph> = MockHook<'env>;
+    }
 }
-impl<'dangling, 'env> SelfRef<'env> for PluginEnvironment<'dangling, 'env> {}
 
-pub struct PluginEnvironmentK<'dangling> {
-    _p: PhantomData<&'dangling ()>,
+/// A plugin environment. An emulated hexchat instance.
+// TODO: maybe make this a full plugin host, with <'app>?
+pub struct PluginEnvironment<'env> {
+    /// The loaded plugins.
+    plugins: RefCell<Vec<Pin<Rc<Holder<'env, MockPluginK<'env>>>>>>,
+    /// Priority-based hooks.
+    hooks: RefCell<Vec<Pin<Rc<Holder<'env, MockHookK<'env>>>>>>,
+    /// Timer and fd hooks.
+    extra_hooks: RefCell<Vec<Pin<Rc<Holder<'env, MockHookK<'env>>>>>>,
+    /// Hook recursion depth.
+    depth: Cell<usize>,
+    /// Contexts.
+    contexts: RefCell<Vec<Rc<Context>>>,
 }
+
+pub struct PluginEnvironmentK;
 opaque! {
-    impl['dangling] Opaque for PluginEnvironmentK<'dangling> {
-        type Kind<'env> = PluginEnvironment<'dangling, 'env>;
+    impl Opaque for PluginEnvironmentK {
+        type Kind<'env> = PluginEnvironment<'env>;
     }
 }
 
-pub type GlobalPluginEnvironment<'dangling> = Holder<'dangling, PluginEnvironmentK<'dangling>>;
-
-unsafe extern "C" fn hexchat_hook_command(
-    ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_hook_command<'ph>(
+    ph: MutLtPtr<'ph, Ph<'ph>>,
     name: *const libc::c_char,
     pri: libc::c_int,
     /* CALLBACK */
@@ -139,10 +165,10 @@ unsafe extern "C" fn hexchat_hook_command(
     help_text: *const libc::c_char,
     userdata: *mut libc::c_void
 ) -> *const crate::internals::HexchatHook {
-    let ph = ph as *mut MockPlugin;
+    let ph = MutLtPtr::from_raw(ph.as_raw() as *mut MockPlugin<'ph, '_>);
     todo!();
 }
-unsafe extern "C" fn hexchat_hook_server(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_hook_server<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         name: *const libc::c_char,
         pri: libc::c_int,
         /* CALLBACK */
@@ -150,7 +176,7 @@ unsafe extern "C" fn hexchat_hook_server(ph: *mut crate::internals::HexchatPlugi
         userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook {
     todo!();
 }
-unsafe extern "C" fn hexchat_hook_print(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_hook_print<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         name: *const libc::c_char,
         pri: libc::c_int,
         /* CALLBACK */
@@ -158,14 +184,14 @@ unsafe extern "C" fn hexchat_hook_print(ph: *mut crate::internals::HexchatPlugin
         userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook {
     todo!();
 }
-unsafe extern "C" fn hexchat_hook_timer(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_hook_timer<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         timeout: libc::c_int,
         /* CALLBACK */
         callback: unsafe extern "C" fn(user_data: *mut libc::c_void) -> libc::c_int,
         userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook {
     todo!();
 }
-unsafe extern "C" fn hexchat_hook_fd(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_hook_fd<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         fd: libc::c_int,
         flags: libc::c_int,
         /* CALLBACK */
@@ -173,80 +199,102 @@ unsafe extern "C" fn hexchat_hook_fd(ph: *mut crate::internals::HexchatPlugin,
         userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook {
     todo!();
 }
-unsafe extern "C" fn hexchat_unhook(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_unhook<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         hook: *const crate::internals::HexchatHook) -> *const libc::c_void {
     todo!();
 }
-unsafe extern "C" fn hexchat_print(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_print<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         text: *const libc::c_char) {
     todo!();
 }
-pub unsafe extern "C" fn hexchat_printf(ph: *mut crate::internals::HexchatPlugin,
+pub unsafe extern "C" fn hexchat_printf<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         format: *const libc::c_char, ...) {
     unimplemented!();
 }
-unsafe extern "C" fn hexchat_command(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_command<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         command: *const libc::c_char) {
     todo!();
 }
-unsafe extern "C" fn hexchat_commandf(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_commandf<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         format: *const libc::c_char, ...) {
     unimplemented!();
 }
-unsafe extern "C" fn hexchat_nickcmp(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_nickcmp<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         s1: *const libc::c_char,
         s2: *const libc::c_char) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_set_context(ph: *mut crate::internals::HexchatPlugin,
-        ctx: *const crate::internals::HexchatContext) -> libc::c_int {
-    todo!();
+unsafe extern "C" fn hexchat_set_context<'ph>(
+    ph: MutLtPtr<'ph, Ph<'ph>>,
+    ctx: *const crate::internals::HexchatContext,
+) -> libc::c_int {
+    let ctx = ctx as *const Context;
+    let ph: *mut MockPlugin<'_, '_> = ph.as_raw() as *mut _;
+    if ctx.is_null() { return 0; }
+    // in C, ctx must NOT be dangling, so we let miri catch it
+    let _ = &*ctx;
+    // in addition to that we also panic if ctx isn't on the list,
+    // because you're supposed to use "Close Context" for cleanup.
+    // if your plugin makes it this far, you're literally passing in garbage.
+    let env: Pin<&PluginEnvironment<'_>> = (*ph).env;
+    if env.contexts.borrow().iter().any(|e| ptr::eq(&**e, ctx)) {
+        (*ph).context = Some(crate::rc_clone_from_raw(ctx));
+        return 1;
+    } else {
+        panic!("garbage in hexchat_set_context - broken plugin")
+    }
 }
-unsafe extern "C" fn hexchat_find_context(ph: *mut crate::internals::HexchatPlugin,
-        servname: *const libc::c_char,
-        channel: *const libc::c_char) -> *const crate::internals::HexchatContext {
+unsafe extern "C" fn hexchat_find_context<'ph>(
+    ph: MutLtPtr<'ph, Ph<'ph>>,
+    servname: *const libc::c_char,
+    channel: *const libc::c_char,
+) -> *const crate::internals::HexchatContext {
     todo!();
 }
-unsafe extern "C" fn hexchat_get_context(ph: *mut crate::internals::HexchatPlugin) -> *const crate::internals::HexchatContext {
-    todo!();
+unsafe extern "C" fn hexchat_get_context<'ph>(
+    ph: MutLtPtr<'ph, Ph<'ph>>,
+) -> *const crate::internals::HexchatContext {
+    let ph: *mut MockPlugin<'_, '_> = ph.as_raw() as *mut _;
+    let ctx = (*ph).context.as_ref().map(|rc| Rc::as_ptr(rc));
+    ctx.unwrap_or(ptr::null()) as *const _
 }
-unsafe extern "C" fn hexchat_get_info(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_get_info<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         id: *const libc::c_char) -> *const libc::c_char {
     todo!();
 }
-unsafe extern "C" fn hexchat_get_prefs(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_get_prefs<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         name: *const libc::c_char,
         string: *mut *const libc::c_char,
         integer: *mut libc::c_int) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_list_get(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_list_get<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         name: *const libc::c_char) -> *mut crate::internals::HexchatList {
     todo!();
 }
-unsafe extern "C" fn hexchat_list_free(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_list_free<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         xlist: *const crate::internals::HexchatList) {
     todo!();
 }
-unsafe extern "C" fn hexchat_list_fields(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_list_fields<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         name: *const libc::c_char) -> *const *const libc::c_char {
     todo!();
 }
-unsafe extern "C" fn hexchat_list_next(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_list_next<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         xlist: *const crate::internals::HexchatList) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_list_str(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_list_str<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         xlist: *const crate::internals::HexchatList,
         name: *const libc::c_char) -> *const libc::c_char {
     todo!();
 }
-unsafe extern "C" fn hexchat_list_int(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_list_int<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         xlist: *const crate::internals::HexchatList,
         name: *const libc::c_char) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_plugingui_add(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_plugingui_add<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         filename: *const libc::c_char,
         name: *const libc::c_char,
         desc: *const libc::c_char,
@@ -254,30 +302,30 @@ unsafe extern "C" fn hexchat_plugingui_add(ph: *mut crate::internals::HexchatPlu
         reserved: *mut char) -> *const crate::internals::PluginGuiHandle {
     todo!();
 }
-unsafe extern "C" fn hexchat_plugingui_remove(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_plugingui_remove<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         handle: *const crate::internals::PluginGuiHandle) {
     todo!();
 }
-unsafe extern "C" fn hexchat_emit_print(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_emit_print<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         event_name: *const libc::c_char, ...) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_read_fd(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_read_fd<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         src: *const libc::c_void,
         buf: *mut char,
         len: *mut libc::c_int) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_list_time(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_list_time<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         xlist: *const crate::internals::HexchatList,
         name: *const libc::c_char) -> libc::time_t {
     todo!();
 }
-unsafe extern "C" fn hexchat_gettext(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_gettext<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         msgid: *const libc::c_char) -> *const libc::c_char {
     todo!();
 }
-unsafe extern "C" fn hexchat_send_modes(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_send_modes<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         targets: *mut *const libc::c_char,
         ntargets: libc::c_int,
         modes_per_line: libc::c_int,
@@ -285,44 +333,44 @@ unsafe extern "C" fn hexchat_send_modes(ph: *mut crate::internals::HexchatPlugin
         mode: libc::c_char) {
     todo!();
 }
-unsafe extern "C" fn hexchat_strip(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_strip<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         string: *const libc::c_char,
         len: libc::c_int,
         flags: libc::c_int) -> *const libc::c_char {
     todo!();
 }
-unsafe extern "C" fn hexchat_free(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_free<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         ptr: *const libc::c_void) {
     todo!();
 }
-unsafe extern "C" fn hexchat_pluginpref_set_str(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_pluginpref_set_str<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         var: *const libc::c_char,
         value: *const libc::c_char) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_pluginpref_get_str(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_pluginpref_get_str<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         var: *const libc::c_char,
         dest: *mut char) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_pluginpref_set_int(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_pluginpref_set_int<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         var: *const libc::c_char,
         value: libc::c_int) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_pluginpref_get_int(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_pluginpref_get_int<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         var: *const libc::c_char) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_pluginpref_delete(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_pluginpref_delete<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         var: *const libc::c_char) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_pluginpref_list(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_pluginpref_list<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         dest: *mut char) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_hook_server_attrs(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_hook_server_attrs<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         name: *const libc::c_char,
         pri: libc::c_int,
         /* CALLBACK */
@@ -330,7 +378,7 @@ unsafe extern "C" fn hexchat_hook_server_attrs(ph: *mut crate::internals::Hexcha
         userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook {
     todo!();
 }
-unsafe extern "C" fn hexchat_hook_print_attrs(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_hook_print_attrs<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         name: *const libc::c_char,
         pri: libc::c_int,
         /* CALLBACK */
@@ -338,106 +386,189 @@ unsafe extern "C" fn hexchat_hook_print_attrs(ph: *mut crate::internals::Hexchat
         userdata: *mut libc::c_void) -> *const crate::internals::HexchatHook {
     todo!();
 }
-unsafe extern "C" fn hexchat_emit_print_attrs(ph: *mut crate::internals::HexchatPlugin, attrs: *const crate::internals::HexchatEventAttrs,
+unsafe extern "C" fn hexchat_emit_print_attrs<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>, attrs: *const crate::internals::HexchatEventAttrs,
         event_name: *const libc::c_char, ...) -> libc::c_int {
     todo!();
 }
-unsafe extern "C" fn hexchat_event_attrs_create(ph: *mut crate::internals::HexchatPlugin) -> *mut crate::internals::HexchatEventAttrs {
+unsafe extern "C" fn hexchat_event_attrs_create<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>) -> *mut crate::internals::HexchatEventAttrs {
     todo!();
 }
-unsafe extern "C" fn hexchat_event_attrs_free(ph: *mut crate::internals::HexchatPlugin,
+unsafe extern "C" fn hexchat_event_attrs_free<'ph>(ph: MutLtPtr<'ph, Ph<'ph>>,
         attrs: *mut crate::internals::HexchatEventAttrs) {
     todo!();
 }
 
-impl<'dangling, 'env> PluginEnvironment<'dangling, 'env> {
-    pub fn new() -> Self {
-        Self {
-            plugins: Default::default(),
-            hooks: Default::default(),
-            extra_hooks: Default::default(),
-            depth: Default::default(),
+impl<'env> PluginEnvironment<'env> {
+    /// Creates a new plugin environment.
+    pub fn new() -> Holder<'static, PluginEnvironmentK> {
+        struct NewEnv;
+        impl<'k> NewWith<'k, PluginEnvironmentK> for NewEnv {
+            fn new_with<'env>(self) -> PluginEnvironment<'env> {
+                PluginEnvironment {
+                    plugins: Default::default(),
+                    hooks: Default::default(),
+                    extra_hooks: Default::default(),
+                    depth: Default::default(),
+                    contexts: Default::default(),
+                }
+            }
+        }
+        Holder::new_with(NewEnv)
+    }
+
+    /// Loads a plugin.
+    pub fn load_plugin<T: PluginFactory>(
+        self: Pin<&'env Self>,
+        filename: CString,
+        arg: Option<&CStr>,
+    ) -> Result<(), ()> {
+        let plug = MockPlugin::new(self, filename);
+        // add the plugin
+        self.plugins.borrow_mut().push(plug.clone());
+
+        struct MockPluginInit<'arg, T> {
+            arg: Option<&'arg CStr>,
+            _p: PhantomData<T>
+        }
+        impl<'k, 'env: 'k, 'arg, T> OperateIn<'k, MockPluginK<'env>>
+        for MockPluginInit<'arg, T>
+        where T: PluginFactory {
+            type Out = bool;
+            fn operate_in<'ph>(
+                self,
+                ph: Pin<&'ph UnsafeCell<MockPlugin<'ph, 'env>>>,
+            ) -> bool where 'k: 'ph {
+                let ptr = ph.get();
+                let arg = self.arg;
+                unsafe {
+                    // setup deinit function
+                    (*ptr).deinit = Some(
+                        crate::hexchat_plugin_deinit::<'ph, T::Plugin<'ph>>
+                    );
+                    crate::hexchat_plugin_init::<'ph, T::Plugin<'ph>>(
+                        MutLtPtr::from_raw(ptr as *mut _),
+                        ptr::addr_of_mut!((*ptr).plugin_name),
+                        ptr::addr_of_mut!((*ptr).plugin_desc),
+                        ptr::addr_of_mut!((*ptr).plugin_vers),
+                        arg.map(|arg| arg.as_ptr()).unwrap_or(ptr::null()),
+                    ) != 0
+                }
+            }
+        }
+        if plug.as_ref().operate_in(MockPluginInit::<T> {
+            arg,
+            _p: PhantomData,
+        }) {
+            Ok(())
+        } else {
+            // remove the plugin - aka retain every plugin that isn't this one
+            self.plugins.borrow_mut().retain(|e| {
+                !ptr::eq::<Holder<_>>(&**e, &*plug)
+            });
+            Err(())
         }
     }
 }
 
-//
-//impl MockPlugin {
-//    pub fn new<T: for<'ph> crate::Plugin<'ph> + Default>(
-//        env: Pin<&PluginEnvironment>,
-//        filename: CString,
-//        arg: Option<&CStr>,
-//    ) -> Option<Pin<Box<MockPlugin>>> {
-//        let filename = filename.into_raw();
-//        let mut plug = Box::pin(MockPlugin {
-//            methods: crate::internals::Ph {
-//                hexchat_hook_command,
-//                hexchat_hook_server,
-//                hexchat_hook_print,
-//                hexchat_hook_timer,
-//                hexchat_hook_fd,
-//                hexchat_unhook,
-//                hexchat_print,
-//                userdata: hexchat_printf as *mut libc::c_void,
-//                hexchat_command,
-//                hexchat_commandf_do_not_use: hexchat_commandf,
-//                hexchat_nickcmp,
-//                hexchat_set_context,
-//                hexchat_find_context,
-//                hexchat_get_context,
-//                hexchat_get_info,
-//                hexchat_get_prefs,
-//                hexchat_list_get,
-//                hexchat_list_free,
-//                hexchat_list_fields,
-//                hexchat_list_next,
-//                hexchat_list_str,
-//                hexchat_list_int,
-//                hexchat_plugingui_add,
-//                hexchat_plugingui_remove,
-//                hexchat_emit_print,
-//                hexchat_read_fd,
-//                hexchat_list_time,
-//                hexchat_gettext,
-//                hexchat_send_modes,
-//                hexchat_strip,
-//                hexchat_free,
-//                hexchat_pluginpref_set_str,
-//                hexchat_pluginpref_get_str,
-//                hexchat_pluginpref_set_int,
-//                hexchat_pluginpref_get_int,
-//                hexchat_pluginpref_delete,
-//                hexchat_pluginpref_list,
-//                hexchat_hook_server_attrs,
-//                hexchat_hook_print_attrs,
-//                hexchat_emit_print_attrs,
-//                hexchat_event_attrs_create,
-//                hexchat_event_attrs_free,
-//            },
-//            filename: filename,
-//            plugin_name: filename,
-//            plugin_desc: ptr::null(),
-//            plugin_vers: ptr::null(),
-//            free_strings: false,
-//            env: &*env,
-//            _pin: PhantomPinned,
-//        });
-//        if unsafe {
-//            let ptr: *mut MockPlugin = &mut *plug;
-//            crate::hexchat_plugin_init::<'_, T>(
-//                crate::LtPhPtr {
-//                    ph: ptr as *mut crate::internals::Ph,
-//                    _lt: PhantomData,
-//                },
-//                ptr::addr_of_mut!((*ptr).plugin_name),
-//                ptr::addr_of_mut!((*ptr).plugin_desc),
-//                ptr::addr_of_mut!((*ptr).plugin_vers),
-//                arg.map(|arg| arg.as_ptr()).unwrap_or(ptr::null()),
-//            ) != 0
-//        } {
-//            Some(plug)
-//        } else {
-//            None
-//        }
-//    }
-//}
+/// Helper for making [`MockPlugin`]s from [`crate::Plugin`]s.
+pub trait PluginFactory: 'static {
+    /// The plugin type.
+    type Plugin<'a>: crate::Plugin<'a> + Default;
+}
+
+impl<'ph, 'env> MockPlugin<'ph, 'env> {
+    /// Creates a MockPlugin, but does not load it (yet).
+    fn new(
+        env: Pin<&'env PluginEnvironment<'env>>,
+        filename: CString,
+    ) -> Pin<Rc<Holder<'env, MockPluginK<'env>>>> {
+        struct MockPluginNewWith<'env> {
+            filename: CString,
+            env: Pin<&'env PluginEnvironment<'env>>
+        }
+        impl<'k, 'env> NewWith<'k, MockPluginK<'env>>
+        for MockPluginNewWith<'env>
+        where 'env: 'k {
+            fn new_with<'ph>(self) -> UnsafeCell<MockPlugin<'ph, 'env>>
+            where 'k: 'ph {
+                let filename = self.filename;
+                let env = self.env;
+                UnsafeCell::new(MockPlugin {
+                    methods: Ph {
+                        hexchat_hook_command,
+                        hexchat_hook_server,
+                        hexchat_hook_print,
+                        hexchat_hook_timer,
+                        hexchat_hook_fd,
+                        hexchat_unhook,
+                        hexchat_print,
+                        userdata: hexchat_printf as *mut _,
+                        hexchat_command,
+                        hexchat_commandf_do_not_use: hexchat_commandf,
+                        hexchat_nickcmp,
+                        hexchat_set_context,
+                        hexchat_find_context,
+                        hexchat_get_context,
+                        hexchat_get_info,
+                        hexchat_get_prefs,
+                        hexchat_list_get,
+                        hexchat_list_free,
+                        hexchat_list_fields,
+                        hexchat_list_next,
+                        hexchat_list_str,
+                        hexchat_list_int,
+                        hexchat_plugingui_add,
+                        hexchat_plugingui_remove,
+                        hexchat_emit_print,
+                        hexchat_read_fd,
+                        hexchat_list_time,
+                        hexchat_gettext,
+                        hexchat_send_modes,
+                        hexchat_strip,
+                        hexchat_free,
+                        hexchat_pluginpref_set_str,
+                        hexchat_pluginpref_get_str,
+                        hexchat_pluginpref_set_int,
+                        hexchat_pluginpref_get_int,
+                        hexchat_pluginpref_delete,
+                        hexchat_pluginpref_list,
+                        hexchat_hook_server_attrs,
+                        hexchat_hook_print_attrs,
+                        hexchat_emit_print_attrs,
+                        hexchat_event_attrs_create,
+                        hexchat_event_attrs_free,
+                    },
+                    filename: filename,
+                    plugin_name: ptr::null(), // not initialized yet!
+                    plugin_desc: ptr::null(),
+                    plugin_vers: ptr::null(),
+                    free_strings: false,
+                    env: env,
+                    deinit: None,
+                    context: None,
+                })
+            }
+        }
+        let plug = Rc::pin(Holder::new_with(MockPluginNewWith {
+            filename,
+            env,
+        }));
+        struct MockPluginPreInit;
+        impl<'k, 'env: 'k> OperateIn<'k, MockPluginK<'env>>
+        for MockPluginPreInit {
+            type Out = ();
+            fn operate_in<'ph>(
+                self,
+                ph: Pin<&'ph UnsafeCell<MockPlugin<'ph, 'env>>>,
+            ) where 'k: 'ph {
+                let ptr = ph.get();
+                unsafe {
+                    // initialize plugin_name from filename(!)
+                    (*ptr).plugin_name = (*ptr).filename.as_ptr();
+                }
+            }
+        }
+        plug.as_ref().operate_in(MockPluginPreInit);
+        plug
+    }
+}