use std::{marker::PhantomData, ptr::NonNull};
#[cfg(feature = "futures")]
use crate::{context::AsyncContext, runtime::AsyncRuntime};
use crate::{qjs, Context, Result, Runtime};
pub trait Intrinsic {
unsafe fn add_intrinsic(ctx: NonNull<qjs::JSContext>);
}
pub struct ContextBuilder<I>(PhantomData<I>);
macro_rules! intrinsic_impls {
(@builtin: $($(#[$meta:meta])* $name:ident $func:ident $(($($args:expr),*))*,)*) => {
$(
$(#[$meta])*
pub struct $name;
impl Intrinsic for $name {
unsafe fn add_intrinsic(ctx: NonNull<qjs::JSContext>) {
qjs::$func(ctx.as_ptr() $(, $($args),*)*);
}
}
)*
};
(@tuple: $($($name:ident)*,)*) => {
$(
impl<$($name,)*> Intrinsic for ($($name,)*)
where
$($name: Intrinsic,)*
{
unsafe fn add_intrinsic(_ctx: NonNull<qjs::JSContext>) {
$($name::add_intrinsic(_ctx);)*
}
}
)*
}
}
pub mod intrinsic {
use super::{qjs, Intrinsic, NonNull};
intrinsic_impls! {
@builtin:
BaseObjects JS_AddIntrinsicBaseObjects,
Date JS_AddIntrinsicDate,
Eval JS_AddIntrinsicEval,
StringNormalize JS_AddIntrinsicStringNormalize,
RegExpCompiler JS_AddIntrinsicRegExpCompiler,
RegExp JS_AddIntrinsicRegExp,
Json JS_AddIntrinsicJSON,
Proxy JS_AddIntrinsicProxy,
MapSet JS_AddIntrinsicMapSet,
TypedArrays JS_AddIntrinsicTypedArrays,
Promise JS_AddIntrinsicPromise,
BigInt JS_AddIntrinsicBigInt,
BigFloat JS_AddIntrinsicBigFloat,
BigDecimal JS_AddIntrinsicBigDecimal,
Operators JS_AddIntrinsicOperators,
BignumExt JS_EnableBignumExt (1),
}
pub type Base = BaseObjects;
pub type None = ();
pub type All = (
Date,
Eval,
StringNormalize,
RegExpCompiler,
RegExp,
Json,
Proxy,
MapSet,
TypedArrays,
Promise,
BigInt,
BigFloat,
BigDecimal,
Operators,
BignumExt,
);
}
intrinsic_impls! {
@tuple:
,
A,
A B,
A B C,
A B C D,
A B C D E,
A B C D E F,
A B C D E F G,
A B C D E F G H,
A B C D E F G H I,
A B C D E F G H I J,
A B C D E F G H I J K,
A B C D E F G H I J K L,
A B C D E F G H I J K L M,
A B C D E F G H I J K L M N,
A B C D E F G H I J K L M N O,
A B C D E F G H I J K L M N O P,
A B C D E F G H I J K L M N O P R,
}
impl Default for ContextBuilder<()> {
fn default() -> Self {
ContextBuilder(PhantomData)
}
}
impl<I: Intrinsic> ContextBuilder<I> {
pub fn with<J: Intrinsic>(self) -> ContextBuilder<(I, J)> {
ContextBuilder(PhantomData)
}
pub fn build(self, runtime: &Runtime) -> Result<Context> {
Context::custom::<I>(runtime)
}
#[cfg(feature = "futures")]
pub async fn build_async(self, runtime: &AsyncRuntime) -> Result<AsyncContext> {
AsyncContext::custom::<I>(runtime).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn all_intrinsinces() {
let rt = crate::Runtime::new().unwrap();
let ctx = Context::builder()
.with::<intrinsic::All>()
.build(&rt)
.unwrap();
let result: usize = ctx.with(|ctx| ctx.eval("1+1")).unwrap();
assert_eq!(result, 2);
}
#[test]
fn custom_intrinsic() {
struct MyIntrinsic;
impl Intrinsic for MyIntrinsic {
unsafe fn add_intrinsic(ctx: NonNull<qjs::JSContext>) {
let ctx = crate::Ctx::from_raw(ctx);
ctx.globals().set("test", 42).unwrap();
}
}
let rt = crate::Runtime::new().unwrap();
let ctx = Context::builder()
.with::<(MyIntrinsic, intrinsic::Eval)>()
.build(&rt)
.unwrap();
let result: usize = ctx.with(|ctx| ctx.eval("test+1")).unwrap();
assert_eq!(result, 43);
}
}