napi 3.12.0-alpha.0

N-API bindings
Documentation
#![cfg(all(
  feature = "async-runtime",
  not(feature = "tokio_rt"),
  not(feature = "noop")
))]

use std::{
  future::Future,
  pin::Pin,
  sync::{
    atomic::{AtomicBool, Ordering},
    Arc,
  },
};

use napi::bindgen_prelude::{
  spawn_blocking_on_custom_runtime, spawn_on_custom_runtime, try_register_async_runtime,
  try_shutdown_async_runtime, try_start_async_runtime, within_selected_async_runtime, AsyncRuntime,
  AsyncRuntimeRejection, AsyncRuntimeTask,
};

struct TestRuntime {
  running: Arc<AtomicBool>,
}

unsafe impl AsyncRuntime for TestRuntime {
  fn spawn(
    &self,
    task: AsyncRuntimeTask,
  ) -> std::result::Result<(), AsyncRuntimeRejection<AsyncRuntimeTask>> {
    Err(AsyncRuntimeRejection::new(
      task,
      napi::Error::from_reason("TestRuntime does not accept tasks"),
    ))
  }

  fn block_on(&self, future: Pin<&mut dyn Future<Output = ()>>) -> napi::Result<()> {
    futures::executor::block_on(future);
    Ok(())
  }

  fn start(&self) -> napi::Result<()> {
    self.running.store(true, Ordering::SeqCst);
    Ok(())
  }

  fn shutdown(&self) -> napi::Result<()> {
    self.running.store(false, Ordering::SeqCst);
    Ok(())
  }
}

#[test]
fn missing_backend_is_rejected_by_operations_not_feature_activation() {
  let error = futures::executor::block_on(spawn_on_custom_runtime(async {}))
    .expect_err("spawning without a backend must preserve the configuration error");
  assert!(error.is_runtime_error());
  assert!(error.to_string().contains("No AsyncRuntime backend"));
  let error = futures::executor::block_on(spawn_blocking_on_custom_runtime(|| ()))
    .expect_err("blocking work without a backend must preserve the configuration error");
  assert!(error.is_runtime_error());
  assert!(error.to_string().contains("No AsyncRuntime backend"));

  let error = try_start_async_runtime().expect_err("runtime use must require registration");
  assert!(error.reason.contains("No AsyncRuntime backend"), "{error}");
  let error = within_selected_async_runtime(|| Ok::<_, napi::Error>(()))
    .expect_err("generated entry guards must require registration");
  assert!(error.reason.contains("No AsyncRuntime backend"), "{error}");

  let running = Arc::new(AtomicBool::new(false));
  try_register_async_runtime(TestRuntime {
    running: Arc::clone(&running),
  })
  .expect("a failed operation without an active environment must not freeze backend selection");
  try_start_async_runtime().unwrap();
  assert!(running.load(Ordering::SeqCst));
  try_shutdown_async_runtime().unwrap();
  assert!(!running.load(Ordering::SeqCst));
}