use crate::base::UnsafeFnOnce;
use crate::js::js_event_loop::{js_create_event_loop_proxy, JsEventLoopProxy};
use crate::js::js_value_util::JsValueHelper;
use crate::js::{FromJsValue, ToJsCallResult, ToJsValue};
use quick_js::{Context, ExecutionError, JsPromise, JsValue, ValueError};
use std::env;
use std::future::Future;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use tokio::runtime::Runtime;
use winit::window::{Cursor, CursorIcon};
pub struct JsContext {
context: Context,
runtime: Runtime,
}
impl JsContext {
pub fn new(context: Context, runtime: Runtime) -> Self {
Self { context, runtime }
}
pub fn create_promise(&mut self) -> (JsValue, PromiseResolver) {
let promise = JsPromise::new(&mut self.context);
let result = promise.js_value();
let elp = js_create_event_loop_proxy();
let resolver = PromiseResolver::new(promise, elp);
(result, resolver)
}
pub fn create_async_task2<F, O>(&mut self, future: F) -> JsValue
where
F: Future<Output = O> + Send + 'static,
O: ToJsCallResult,
{
let (result, resolver) = self.create_promise();
self.runtime.spawn(async move {
let res = future.await;
match res.to_js_call_result() {
Ok(r) => resolver.resolve(r),
Err(e) => resolver.reject(JsValue::String(format!("js call error:{:?}", e))),
}
});
result
}
pub fn execute_main(&mut self) {
let module_name = env::var("DEFT_ENTRY").unwrap_or("index.js".to_string());
self.context.execute_module(&module_name).unwrap();
}
pub fn execute_module(&mut self, module_name: &str) -> Result<(), ExecutionError> {
self.context.execute_module(&module_name)
}
pub fn execute_pending_job(&self) -> Result<bool, ExecutionError> {
self.context.execute_pending_job()
}
}
impl Deref for JsContext {
type Target = Context;
fn deref(&self) -> &Self::Target {
&self.context
}
}
impl DerefMut for JsContext {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.context
}
}
pub struct PromiseResolver {
promise: Option<*mut JsPromise>,
event_loop_proxy: JsEventLoopProxy,
}
impl PromiseResolver {
pub fn new(promise: JsPromise, event_loop_proxy: JsEventLoopProxy) -> Self {
Self {
promise: Some(Box::into_raw(Box::new(promise))),
event_loop_proxy,
}
}
pub fn resolve(mut self, value: JsValue) {
unsafe {
let p = self.promise.take().unwrap();
let callback = UnsafeFnOnce::new(move || {
let mut promise = Box::from_raw(p);
promise.resolve(value)
});
self.event_loop_proxy
.schedule_macro_task(callback.into_box())
.unwrap();
}
}
pub fn settle(self, result: Result<JsValue, String>) {
match result {
Ok(v) => self.resolve(v),
Err(e) => self.reject(JsValue::String(e)),
}
}
pub fn reject(mut self, value: JsValue) {
unsafe {
let p = self.promise.take().unwrap();
let callback = UnsafeFnOnce::new(move || {
let mut promise = Box::from_raw(p);
promise.reject(value)
});
self.event_loop_proxy
.schedule_macro_task(callback.into_box())
.unwrap();
}
}
}
unsafe impl Send for PromiseResolver {}
unsafe impl Sync for PromiseResolver {}
impl Drop for PromiseResolver {
fn drop(&mut self) {
if let Some(p) = self.promise {
let callback = unsafe {
UnsafeFnOnce::new(move || {
let _ = Box::from_raw(p);
})
};
self.event_loop_proxy
.schedule_macro_task(callback.into_box())
.unwrap();
}
}
}
pub trait JsValueView {
fn as_bool(&self) -> Option<bool>;
fn as_number_array(&self) -> Option<Vec<f32>>;
}
impl JsValueView for JsValue {
fn as_bool(&self) -> Option<bool> {
match &self {
JsValue::Bool(v) => Some(*v),
_ => None,
}
}
fn as_number_array(&self) -> Option<Vec<f32>> {
if let JsValue::Array(a) = self {
let mut result = Vec::with_capacity(a.len());
for e in a {
result.push(e.as_number()? as f32);
}
Some(result)
} else {
None
}
}
}
impl crate::js::js_binding::FromJsValue for CursorIcon {
fn from_js_value(value: JsValue) -> Result<Self, ValueError> {
match value {
JsValue::String(str) => {
CursorIcon::from_str(&str).map_err(|_e| ValueError::UnexpectedType)
}
_ => Err(ValueError::UnexpectedType),
}
}
}
impl crate::js::js_binding::ToJsValue for CursorIcon {
fn to_js_value(self) -> Result<JsValue, ValueError> {
Ok(JsValue::String(self.name().to_string()))
}
}
impl ToJsValue for Cursor {
fn to_js_value(self) -> Result<JsValue, ValueError> {
match self {
Cursor::Icon(icon) => icon.to_js_value(),
Cursor::Custom(_) => Ok(JsValue::Null),
}
}
}
impl FromJsValue for Cursor {
fn from_js_value(value: JsValue) -> Result<Self, ValueError> {
if let Ok(icon) = CursorIcon::from_js_value(value) {
Ok(Cursor::Icon(icon))
} else {
Err(ValueError::UnexpectedType)
}
}
}