wrapped_mono 0.4.0

`wrapped_mono` is a safe, lightweight wrapper around the mono library. It allows embedding of the mono runtime inside a rust project. Inside this embedded runtime code written in languages supporting the .NET framework, such as C# and F#, can be run. This allows usage of libraries written in those languages, and using them as a scripting language. The mono runtime is used by many game engines, and this wrapper allows using it with projects written in Rust too.
Documentation
use crate::assembly::Assembly;
use crate::binds::{mono_domain_assembly_open, mono_domain_create, MonoDomain};
use crate::jit::esnure_thread_registered;
#[cfg(test)]
use crate::TEST_DOMAIN;
/// Safe representation of [`MonoDomain`] type.
#[derive(Eq, Clone, Copy)]
pub struct Domain {
    ptr: *mut MonoDomain,
}
use std::ffi::CString;
use std::sync::LazyLock;
impl Domain {
    /// Loads [`Assembly`] at path into domain, returns **None** if assembly could not be loaded(is missing or broken), and `Some(Assembly)` if it was successfully loaded.
    #[must_use]
    pub fn assembly_open(&self, path: &str) -> Option<Assembly> {
        //! # Example
        //!```no_run
        //! # use wrapped_mono::*;
        //! # let domain = jit::init("name",None);
        //! let asm = domain.assembly_open("SomeAssembly.dll").expect("Could not load assembly!");
        //!```
        esnure_thread_registered();
        self.attach_thread();
        let cstr = CString::new(path).expect(crate::STR2CSTR_ERR);
        let ptr = unsafe { mono_domain_assembly_open(self.get_ptr(), cstr.as_ptr()) };
        if ptr.is_null() {
            return None;
        }
        let _ = &cstr;
        Some(unsafe { Assembly::from_ptr(ptr) })
    }
    /// Creates a new empty domain
    /// # Example
    /// ```no_run
    /// # use wrapped_mono::*;
    /// let domain1 = jit::init("name",None);
    /// let domain2 = Domain::create();
    /// ```
    #[must_use]
    pub fn create() -> Self {
        unsafe { Self::from_ptr(mono_domain_create()) }
    }
    /// Sets domain confing to one loaded from file *filename* in directory *`base_directory`*.
    pub fn set_config(&self, base_directory: &str, filename: &str) {
        let bd_cstr = CString::new(base_directory).expect(crate::STR2CSTR_ERR);
        let fnme_cstr = CString::new(filename).expect(crate::STR2CSTR_ERR);
        unsafe {
            crate::binds::mono_domain_set_config(self.ptr, bd_cstr.as_ptr(), fnme_cstr.as_ptr());
        }
        drop(bd_cstr);
        drop(fnme_cstr);
    }
    /// Function creating [`Domain`] type from a pointer to [`MonoDomain`].
    /// # Safety
    /// Pointer must be a valid pointer to [`MonoDomain`].
    pub unsafe fn from_ptr(ptr: *mut MonoDomain) -> Self {
        Self { ptr }
    }
    /// Function returning internal pointer to [`MonoDomain`]
    #[must_use]
    pub fn get_ptr(&self) -> *mut MonoDomain {
        self.ptr
    }
    /// Sets domain as the current domain.
    pub fn set_current(&self, active: bool) {
        unsafe { crate::binds::mono_domain_set(self.ptr, i32::from(active)) };
    }
    /// Attaches current thread (makes domain "aware" of this threads existence, allowing domain to eg. automatically stop it during garbage collection to prevent errors.) Should be done for all threads that will interact with this domain.  
    pub fn attach_thread(&self) {
        unsafe { crate::binds::mono_jit_thread_attach(self.ptr) };
    }
    /* TODO: fix domain unloading/freeing
    /// [DOES not work]
    fn unload(self){
        self.set(true);
        self.attach();
        unsafe{crate::binds::mono_domain_unload(self.ptr)};
        drop(self);
    }
    /// Releases resources related to a specific domain. If *force* is true, allows releasing of the root domain. Used during shut-down.
    /// # Safety
    /// Since this function releases all resources related to given domain, it means that all references to objects inside it will become invalid.
    fn free(self,force:bool){
        unsafe{crate::binds::mono_domain_free(self.ptr,force as i32)};
        drop(self);
    }
    */
    /// Returns current domain or `None` if mono runtime is not initialized yet.
    #[must_use]
    pub fn get_current() -> Option<Self> {
        let ptr = unsafe { crate::binds::mono_domain_get() };
        if ptr.is_null() {
            None
        } else {
            unsafe { Some(Self::from_ptr(ptr)) }
        }
    }
}
// Allows you to compare two domains to check if they are one and the same.
impl std::cmp::PartialEq for Domain {
    fn eq(&self, other: &Self) -> bool {
        self.ptr == other.ptr
    }
}

unsafe impl Send for Domain {}
#[test]
fn eq() {
    let a = TEST_DOMAIN.lock().unwrap();
    let a = *a;
    let b = Domain::create();
    assert!(a == a);
    assert!(b != a);
    assert!(a != b);
    assert!(b == b);
}
#[test]
fn get_set_current() {
    let domain = TEST_DOMAIN.lock().unwrap();
    domain.attach_thread();
    let domain = *domain;
    domain.set_current(true);
    assert!(domain == Domain::get_current().unwrap());
}