1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#![feature(unboxed_closures)]
#![feature(tuple_trait)]
#![feature(fn_traits)]
#![feature(exit_status_error)]

use std::{marker::Tuple, process::{Command, Stdio}, collections::HashMap, any::Any, fmt::Debug};
use libloading::{Library, Symbol};
use parking_lot::{RwLock, RwLockWriteGuard};

pub use hot_potato_proc_macro::potato;
pub use inventory::submit;

type InitFn = for<'a> fn(RwLockWriteGuard<'a, Option<HashMap<&'static str, Box<dyn Any>>>>);

pub struct PotatoFunc<Args: Tuple, MagicArgs: Tuple, Output, F: Fn<MagicArgs, Output = Output> + Copy>  {
    path: &'static str,
    func: RwLock<Option<Box<dyn Fn<MagicArgs, Output = Output>>>>,
    magics: RwLock<Option<HashMap<&'static str, Box<dyn Any>>>>,
    initializer: Option<InitFn>,
    mapper: for<'a> fn(Args, &Self) -> MagicArgs,
    _dummy: Option<F>
}

impl<Args: Tuple, MagicArgs: Tuple, Output, F: Fn<MagicArgs, Output = Output> + Copy> PotatoFunc<Args, MagicArgs, Output, F> {
    /// # Safety
    /// DO NOT USE MANUALLY! Only meant for macro use!
    pub const unsafe fn new(path: &'static str, initializer: InitFn, mapper: for<'a> fn(Args, &Self) -> MagicArgs) -> Self {
        
        Self { 
            path, 
            func: RwLock::new(None),
            magics: RwLock::new(None),
            initializer: Some(initializer),
            mapper,
            _dummy: None
        }
    }

    /// # Safety
    /// DO NOT USE MANUALLY! Only meant for macro use!
    pub const unsafe fn handle(&self) -> PotatoHandle{
        PotatoHandle { 
            potato: self as *const _ as *const u8,
            loader: |potato, potato_lib| {
                let potato = unsafe { &mut *(potato as *mut Self) };
                potato.load_from_lib(potato_lib)
            }
        }
    }

    fn load_from_lib(&self, potato_lib: &Library) {
        let fun: Symbol<F> = unsafe { potato_lib.get(self.path.as_ref()).expect("could not find potatoed function") };
        let boxed: Box<dyn Fn<MagicArgs, Output = Output>> = Box::new(*fun);
        let boxed: Box<dyn Fn<MagicArgs, Output = Output>> = unsafe{ std::mem::transmute(boxed) };

        let mut func = self.func.write();
        *func = Some(boxed);
    }
    pub fn get<T: Clone + 'static>(&self, magic: &str) -> T {
        let reader = self.magics.read();
        let map = reader.as_ref().expect("function was not initalized");
        let any = map.get(magic).unwrap_or_else(|| panic!("invalid magic `{}` does not exist!", magic));
        let v: &T = any.downcast_ref().expect("type mismatch while getting magic!");
        v.clone()
    }

    pub fn set<T: Clone + 'static>(&self, magic: &'static str, v: T){
        // easiest way to get all the checks for free!
        self.get::<T>(magic);

        let mut writer = self.magics.write();
        let map = writer.as_mut().expect("function was not initalized");
        map.insert(magic, Box::new(v));
    }
}

pub struct PotatoHandle {
    potato: *const u8,
    loader: fn(*const u8, &Library)
}

unsafe impl Sync for PotatoHandle {}

inventory::collect!(PotatoHandle);

static mut LIBHOLDER: Option<Library> = None;

pub fn build_and_reload_potatoes() -> Result<(), String> {
    // aquire write locks so that no one tries to run a function while the lib is reloading
    let mut locks = vec![];
    for potato_handle in inventory::iter::<PotatoHandle> {
        let potato = unsafe { &mut *(potato_handle.potato as *mut PotatoFunc<(), (), (), fn()>) };
        locks.push((potato.magics.write(), potato_handle));
    }

    // drop old lib
    unsafe { LIBHOLDER.take(); }

    let mut compile = Command::new("cargo").args(["rustc", "--lib", "--", "--crate-type", "cdylib"])
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn().map_err(|e| e.to_string())?;
    let status = compile.wait().expect("did not complete successfully");
    status.exit_ok().map_err(|e| e.to_string())?;

    let next = std::env::args().next().unwrap();
    let (base, exe) = next.rsplit_once(std::path::MAIN_SEPARATOR).unwrap();
    let name = exe.rsplit_once('.').unwrap_or((exe, "")).0;

    #[cfg(windows)]
    let lib = format!("{name}.dll");
    #[cfg(not(windows))]
    let lib = format!("lib{name}.so");

    let potato_lib = unsafe { Library::new(format!("{base}/{lib}")).map_err(|e| e.to_string())? };

    for (magics, potato_handle) in locks {
        (potato_handle.loader)(potato_handle.potato, &potato_lib);
        let potato = unsafe { &mut *(potato_handle.potato as *mut PotatoFunc<(), (), (), fn()>) };
        if let Some(initializer) = potato.initializer.take() {
            initializer(magics)
        }
    }

    unsafe { LIBHOLDER = Some(potato_lib); }

    Ok(())
}

impl<Args: Tuple, MagicArgs: Tuple, Output, F: Fn<MagicArgs, Output = Output> + Copy> FnOnce<Args> for PotatoFunc<Args, MagicArgs, Output, F> {
    type Output = Output;
    extern "rust-call" fn call_once(self, args: Args) -> Self::Output {
        self.func.read().as_ref().map(|f| f.call((self.mapper)(args, &self))).expect("function was not loaded")
    }
}

impl<Args: Tuple, MagicArgs: Tuple, Output, F: Fn<MagicArgs, Output = Output> + Copy> FnMut<Args> for PotatoFunc<Args, MagicArgs, Output, F> {
    extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output {
        self.func.read().as_ref().map(|f| f.call((self.mapper)(args, self))).expect("function was not loaded")
    }
}

impl<Args: Tuple + Debug, MagicArgs: Tuple + Debug, Output, F: Fn<MagicArgs, Output = Output> + Copy> Fn<Args> for PotatoFunc<Args, MagicArgs, Output, F> {
    extern "rust-call" fn call(&self, args: Args) -> Self::Output {
        let margs = (self.mapper)(args, self);
        self.func.read().as_ref().map(|f| f.call(margs)).expect("function was not loaded")
    }
}


unsafe impl <Args: Tuple, MagicArgs: Tuple, Output, F: Fn<MagicArgs, Output = Output> + Copy> Sync for PotatoFunc<Args, MagicArgs, Output, F> {}