unrust 0.1.5

The rust side of the unity package to work with rust + bevy in unity!
Documentation
use std::ffi::{c_char, CStr, CString};

mod loader;
mod logger;
mod unity;

pub use bevy;

use bevy::prelude::*;
pub use codegen::generate_csharp;
pub use inbuilt::*;
pub use loader::GamePlugin;
pub use tracing;

pub use unity::{CreateFn, DestroyEntity, DestroyFn, InstantiateEntity, PrefabData, UpdateFn};
pub use unrust_proc_macro::*;

use crate::{
    logger::{setup_logging, teardown_logging, LoggerFunc},
    unity::{start_app, UnityPlugins},
};

#[repr(C)]
pub struct UnrustContextWrapper;

pub struct UnrustContext {
    pub app: App,
}

static mut GAMEPLUGIN: Option<Box<dyn GamePlugin>> = None;

#[allow(clippy::missing_safety_doc)]
pub unsafe fn setup_game(game: Box<dyn GamePlugin>) {
    GAMEPLUGIN = Some(game);
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn load(logger: LoggerFunc) -> *mut UnrustContextWrapper {
    setup_logging(Box::new(move |level, str| {
        let len = str.len();
        let out_str = CString::new(str).unwrap();
        let out_str = out_str.into_raw();
        (logger)(level, out_str, len);
        unsafe {
            let _ = CString::from_raw(out_str); // drop the string
        }
    }));

    let app = App::new();

    tracing::info!("setting up!");

    let ctx = Box::new(UnrustContext { app });

    Box::into_raw(ctx) as *mut UnrustContextWrapper
}

#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn init(
    ctx: *mut UnrustContextWrapper,
    base_path: *const c_char,
    create: CreateFn,
    update: UpdateFn,
    destroy: DestroyFn,
) {
    let ctx = unsafe { Box::leak(Box::from_raw(ctx as *mut UnrustContext)) };
    let base_path = unsafe { get_string(base_path) };
    ctx.app
        .add_plugins(UnityPlugins::new(base_path, create, update, destroy));

    unsafe {
        if let Some(game) = &GAMEPLUGIN {
            game.initialize(&mut ctx.app);
        } else {
            tracing::info!("game  not setup!");
        }
    }

    start_app(&mut ctx.app);
}

#[no_mangle]
pub extern "C" fn register_prefabs(ctx: *mut UnrustContextWrapper, prefabs: PrefabData) {
    let ctx = unsafe { Box::leak(Box::from_raw(ctx as *mut UnrustContext)) };
    unsafe {
        if let Some(game) = &GAMEPLUGIN {
            game.register(&mut ctx.app.world, prefabs);
        }
    }
}

#[allow(clippy::missing_safety_doc)]
#[no_mangle]
pub unsafe extern "C" fn spawn(
    ctx: *mut UnrustContextWrapper,
    unity_entity: UnityEntity,
    inbuilt: *const InbuiltData,
    len: usize,
    custom: *const u8,
    custom_len: usize,
    custom_state: *const u8,
    custom_state_len: usize,
) -> u64 {
    let components = unsafe { std::slice::from_raw_parts(inbuilt, len) };
    let ctx = unsafe { Box::leak(Box::from_raw(ctx as *mut UnrustContext)) };
    let mut entity = ctx.app.world.spawn_empty();
    entity.insert(unity_entity);
    ingest_component(&mut entity, components);

    unsafe {
        if let Some(game) = &GAMEPLUGIN {
            game.spawn_custom(
                &mut entity,
                custom,
                custom_len,
                custom_state,
                custom_state_len,
            );
        }
    }

    entity.id().to_bits()
}

#[no_mangle]
pub extern "C" fn tick(ctx: *mut UnrustContextWrapper) {
    let ctx = unsafe { Box::leak(Box::from_raw(ctx as *mut UnrustContext)) };
    ctx.app.update();
}

#[no_mangle]
pub extern "C" fn unload(ctx: *mut UnrustContextWrapper) {
    teardown_logging();
    let _ = unsafe { Box::from_raw(ctx as *mut UnrustContext) };
    unsafe { GAMEPLUGIN = None };
}

unsafe fn get_string(base_path: *const i8) -> String {
    CStr::from_ptr(base_path).to_string_lossy().to_string()
}