4afb7f60创建于 2024年9月30日历史提交
/*
 * The MIT License (MIT)
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 */

use napi::threadsafe_function::ThreadSafeCallContext;
use napi::{
  bindgen_prelude::*,
  threadsafe_function::{ErrorStrategy, ThreadsafeFunction, ThreadsafeFunctionCallMode},
  JsBoolean, JsString,
};
use napi_derive::napi;
use std::thread;

#[napi]
pub fn call_threadsafe_function(callback: JsFunction) -> Result<()> {
  let tsfn: ThreadsafeFunction<u32, ErrorStrategy::CalleeHandled> =
    callback.create_threadsafe_function(0, |ctx| Ok(vec![ctx.value + 1]))?;
  for n in 0..100 {
    let tsfn = tsfn.clone();
    thread::spawn(move || {
      tsfn.call(Ok(n), ThreadsafeFunctionCallMode::NonBlocking);
    });
  }
  Ok(())
}

#[napi]
pub fn call_threadsafe_function_multi_args1(callback: JsFunction) -> Result<()> {
  let tsfn: ThreadsafeFunction<(u8, String), ErrorStrategy::Fatal> = callback
    .create_threadsafe_function(0, |ctx: ThreadSafeCallContext<(u8, String)>| {
      let arg1 = ctx.env.create_uint32(ctx.value.0 as u32)?.into_unknown();
      let arg2 = ctx.env.create_string(ctx.value.1.as_str())?.into_unknown();
      Ok(vec![arg1, arg2])
    })?;

  let tsfn = tsfn.clone();
  thread::spawn(move || {
    tsfn.call(
      (100, "hello napi 1".to_string()),
      ThreadsafeFunctionCallMode::Blocking,
    );
  });
  Ok(())
}

#[napi]
pub fn call_threadsafe_function_multi_args2(
  func: ThreadsafeFunction<(u8, String), ErrorStrategy::Fatal>,
) -> Result<()> {
  thread::spawn(move || {
    func.call(
      (100, "hello napi 2".to_string()),
      ThreadsafeFunctionCallMode::Blocking,
    );
  });
  Ok(())
}

#[napi]
pub fn threadsafe_function_throw_error(cb: JsFunction) -> Result<()> {
  let tsfn: ThreadsafeFunction<bool, ErrorStrategy::CalleeHandled> =
    cb.create_threadsafe_function(0, |ctx| ctx.env.get_boolean(ctx.value).map(|v| vec![v]))?;
  thread::spawn(move || {
    tsfn.call(
      Err(Error::new(
        Status::GenericFailure,
        "ThrowFromNative".to_owned(),
      )),
      ThreadsafeFunctionCallMode::Blocking,
    );
  });
  Ok(())
}

#[napi]
pub fn threadsafe_function_fatal_mode(cb: JsFunction) -> Result<()> {
  let tsfn: ThreadsafeFunction<bool, ErrorStrategy::Fatal> =
    cb.create_threadsafe_function(0, |ctx| ctx.env.get_boolean(ctx.value).map(|v| vec![v]))?;
  thread::spawn(move || {
    tsfn.call(true, ThreadsafeFunctionCallMode::Blocking);
  });
  Ok(())
}

#[napi]
pub fn threadsafe_function_fatal_mode_error(cb: JsFunction) -> Result<()> {
  let tsfn: ThreadsafeFunction<bool, ErrorStrategy::Fatal> =
    cb.create_threadsafe_function(0, |_ctx| {
      Err::<Vec<JsBoolean>, Error>(Error::new(
        Status::GenericFailure,
        "Generic tsfn error".to_owned(),
      ))
    })?;
  thread::spawn(move || {
    tsfn.call(true, ThreadsafeFunctionCallMode::Blocking);
  });
  Ok(())
}

#[napi]
fn threadsafe_function_closure_capture(func: JsFunction) -> napi::Result<()> {
  let str = "threadsafe_function_closure_capture";
  let tsfn: ThreadsafeFunction<()> = func.create_threadsafe_function(0, move |ctx| {
    let x = ctx.env.create_string(str)?;
    Ok(vec![x])
  })?;

  tsfn.call(Ok(()), ThreadsafeFunctionCallMode::NonBlocking);

  Ok(())
}

#[napi]
pub fn tsfn_call_with_callback(func: JsFunction) -> napi::Result<()> {
  let tsfn: ThreadsafeFunction<()> =
    func.create_threadsafe_function(0, move |_| Ok(Vec::<JsString>::new()))?;
  tsfn.call_with_return_value(
    Ok(()),
    ThreadsafeFunctionCallMode::NonBlocking,
    |value: String| {
      assert_eq!(value, "ReturnFromJavaScriptRawCallback".to_owned());
      Ok(())
    },
  );
  Ok(())
}

#[napi(ts_return_type = "Promise<string>")]
pub fn tsfn_async_call(env: Env, func: JsFunction) -> napi::Result<Object> {
  let tsfn: ThreadsafeFunction<()> =
    func.create_threadsafe_function(0, move |_| Ok(vec![0u32, 1u32, 2u32]))?;

  env.spawn_future(async move {
    let msg: String = tsfn.call_async(Ok(())).await?;
    assert_eq!(msg, "ReturnFromJavaScriptRawCallback".to_owned());
    Ok(msg + "[rust]")
  })
}

#[napi]
pub fn accept_threadsafe_function(func: ThreadsafeFunction<u32>) {
  thread::spawn(move || {
    func.call(Ok(1), ThreadsafeFunctionCallMode::NonBlocking);
  });
}

#[napi]
pub fn accept_threadsafe_function_fatal(func: ThreadsafeFunction<u32, ErrorStrategy::Fatal>) {
  thread::spawn(move || {
    func.call(1, ThreadsafeFunctionCallMode::NonBlocking);
  });
}

#[napi]
pub fn accept_threadsafe_function_tuple_args(func: ThreadsafeFunction<(u32, bool, String)>) {
  thread::spawn(move || {
    func.call(
      Ok((1, false, "NAPI-RS".into())),
      ThreadsafeFunctionCallMode::NonBlocking,
    );
  });
}

#[napi]
pub async fn tsfn_return_promise(func: ThreadsafeFunction<u32>) -> Result<u32> {
  let val = func.call_async::<Promise<u32>>(Ok(1)).await?.await?;
  Ok(val + 2)
}

#[napi]
pub async fn tsfn_return_promise_timeout(func: ThreadsafeFunction<u32>) -> Result<u32> {
  use tokio::time::{self, Duration};
  let promise = func.call_async::<Promise<u32>>(Ok(1)).await?;
  let sleep = time::sleep(Duration::from_nanos(1));
  tokio::select! {
    _ = sleep => {
      Err(Error::new(Status::GenericFailure, "Timeout".to_owned()))
    }
    value = promise => {
      Ok(value? + 2)
    }
  }
}

#[napi]
pub async fn tsfn_throw_from_js(tsfn: ThreadsafeFunction<u32>) -> napi::Result<u32> {
  tsfn.call_async::<Promise<u32>>(Ok(42)).await?.await
}