use std::future::Future;
use std::os::raw::c_void;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};
use libc::size_t;
use crate::bridge::{GoString, GoTransform};
use crate::wrapper::{Message, SliceContainer, StrContainer, TransformOptions, TransformResult};
struct TransformInvocationData {
src_vec_arc_raw: Option<*const Vec<u8>>,
opt_arc_raw: Option<*const TransformOptions>,
cb_trait_ptr: *mut c_void,
}
extern "C" fn transform_callback(
raw_cb_data: *mut c_void,
code: StrContainer,
map: StrContainer,
raw_errors: *mut Message,
errors_len: size_t,
raw_warnings: *mut Message,
warnings_len: size_t,
) -> () {
unsafe {
let cb_data: Box<TransformInvocationData> = Box::from_raw(raw_cb_data as *mut _);
if let Some(ptr) = cb_data.src_vec_arc_raw {
let _: Arc<Vec<u8>> = Arc::from_raw(ptr);
};
if let Some(ptr) = cb_data.opt_arc_raw {
let _: Arc<TransformOptions> = Arc::from_raw(ptr);
};
let rust_cb_trait_box: Box<Box<dyn FnOnce(TransformResult)>> =
Box::from_raw(cb_data.cb_trait_ptr as *mut _);
let errors = SliceContainer {
ptr: raw_errors,
len: errors_len,
};
let warnings = SliceContainer {
ptr: raw_warnings,
len: warnings_len,
};
rust_cb_trait_box(TransformResult {
code,
map,
errors,
warnings,
});
};
}
unsafe fn call_ffi_transform(
cb_data: *mut TransformInvocationData,
go_code: GoString,
options: &TransformOptions,
) -> () {
#[cfg(target_env = "msvc")]
#[allow(non_snake_case)]
let GoTransform =
std::mem::transmute::<_, GoTransform>(crate::bridge::DLL.get_function("GoTransform"));
GoTransform(
libc::malloc,
transform_callback,
cb_data as *mut c_void,
go_code,
options.ffiapi_ptr,
);
}
pub unsafe fn transform_direct_unmanaged<F>(code: &[u8], options: &TransformOptions, cb: F) -> ()
where
F: FnOnce(TransformResult),
{
let go_code = GoString::from_bytes_unmanaged(code);
let cb_box = Box::new(cb) as Box<dyn FnOnce(TransformResult)>;
let cb_trait_box = Box::new(cb_box);
let cb_trait_ptr = Box::into_raw(cb_trait_box);
let data = Box::into_raw(Box::new(TransformInvocationData {
src_vec_arc_raw: None,
opt_arc_raw: None,
cb_trait_ptr: cb_trait_ptr as *mut c_void,
}));
call_ffi_transform(data, go_code, options);
}
pub fn transform_direct<F>(code: Arc<Vec<u8>>, options: Arc<TransformOptions>, cb: F) -> ()
where
F: FnOnce(TransformResult),
F: Send + 'static,
{
let go_code = unsafe { GoString::from_bytes_unmanaged(&code) };
let cb_box = Box::new(cb) as Box<dyn FnOnce(TransformResult)>;
let cb_trait_box = Box::new(cb_box);
let cb_trait_ptr = Box::into_raw(cb_trait_box);
let data = Box::into_raw(Box::new(TransformInvocationData {
src_vec_arc_raw: Some(Arc::into_raw(code.clone())),
opt_arc_raw: Some(Arc::into_raw(options.clone())),
cb_trait_ptr: cb_trait_ptr as *mut c_void,
}));
unsafe {
call_ffi_transform(data, go_code, options.as_ref());
};
}
struct TransformFutureState {
result: Option<TransformResult>,
waker: Option<Waker>,
}
pub struct TransformFuture {
state: Arc<Mutex<TransformFutureState>>,
}
pub fn transform(code: Arc<Vec<u8>>, options: Arc<TransformOptions>) -> TransformFuture {
let state = Arc::new(Mutex::new(TransformFutureState {
result: None,
waker: None,
}));
let state_cb_copy = state.clone();
transform_direct(code, options, move |result| {
let mut state = state_cb_copy.lock().unwrap();
state.result = Some(result);
if let Some(waker) = state.waker.take() {
waker.wake();
};
});
TransformFuture { state }
}
impl Future for TransformFuture {
type Output = TransformResult;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut state = self.state.lock().unwrap();
match state.result.take() {
Some(result) => Poll::Ready(result),
None => {
state.waker = Some(cx.waker().clone());
Poll::Pending
}
}
}
}