-
Notifications
You must be signed in to change notification settings - Fork 249
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(driver-adapters): move napi-specific code into "napi" module, pr…
…epare empty "wasm" module
- Loading branch information
Showing
11 changed files
with
1,722 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
use serde::Serialize; | ||
use serde_json::value::Value as JsonValue; | ||
|
||
#[derive(Debug, PartialEq, Serialize)] | ||
#[serde(untagged)] | ||
pub enum JSArg { | ||
Value(serde_json::Value), | ||
Buffer(Vec<u8>), | ||
Array(Vec<JSArg>), | ||
} | ||
|
||
impl From<JsonValue> for JSArg { | ||
fn from(v: JsonValue) -> Self { | ||
JSArg::Value(v) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
pub(crate) mod js_arg; | ||
|
||
pub(crate) mod mysql; | ||
pub(crate) mod postgres; | ||
pub(crate) mod sqlite; | ||
|
||
pub use js_arg::JSArg; |
70 changes: 70 additions & 0 deletions
70
query-engine/driver-adapters/src/napi/async_js_function.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
use std::marker::PhantomData; | ||
|
||
use napi::{ | ||
bindgen_prelude::*, | ||
threadsafe_function::{ErrorStrategy, ThreadsafeFunction}, | ||
}; | ||
|
||
use super::{ | ||
error::{async_unwinding_panic, into_quaint_error}, | ||
result::JsResult, | ||
}; | ||
|
||
/// Wrapper for napi-rs's ThreadsafeFunction that is aware of | ||
/// JS drivers conventions. Performs following things: | ||
/// - Automatically unrefs the function so it won't hold off event loop | ||
/// - Awaits for returned Promise | ||
/// - Unpacks JS `Result` type into Rust `Result` type and converts the error | ||
/// into `quaint::Error`. | ||
/// - Catches panics and converts them to `quaint:Error` | ||
pub(crate) struct AsyncJsFunction<ArgType, ReturnType> | ||
where | ||
ArgType: ToNapiValue + 'static, | ||
ReturnType: FromNapiValue + 'static, | ||
{ | ||
threadsafe_fn: ThreadsafeFunction<ArgType, ErrorStrategy::Fatal>, | ||
_phantom: PhantomData<ReturnType>, | ||
} | ||
|
||
impl<ArgType, ReturnType> AsyncJsFunction<ArgType, ReturnType> | ||
where | ||
ArgType: ToNapiValue + 'static, | ||
ReturnType: FromNapiValue + 'static, | ||
{ | ||
fn from_threadsafe_function( | ||
mut threadsafe_fn: ThreadsafeFunction<ArgType, ErrorStrategy::Fatal>, | ||
env: Env, | ||
) -> napi::Result<Self> { | ||
threadsafe_fn.unref(&env)?; | ||
|
||
Ok(AsyncJsFunction { | ||
threadsafe_fn, | ||
_phantom: PhantomData, | ||
}) | ||
} | ||
|
||
pub(crate) async fn call(&self, arg: ArgType) -> quaint::Result<ReturnType> { | ||
let js_result = async_unwinding_panic(async { | ||
let promise = self | ||
.threadsafe_fn | ||
.call_async::<Promise<JsResult<ReturnType>>>(arg) | ||
.await?; | ||
promise.await | ||
}) | ||
.await | ||
.map_err(into_quaint_error)?; | ||
js_result.into() | ||
} | ||
} | ||
|
||
impl<ArgType, ReturnType> FromNapiValue for AsyncJsFunction<ArgType, ReturnType> | ||
where | ||
ArgType: ToNapiValue + 'static, | ||
ReturnType: FromNapiValue + 'static, | ||
{ | ||
unsafe fn from_napi_value(napi_env: napi::sys::napi_env, napi_val: napi::sys::napi_value) -> napi::Result<Self> { | ||
let env = Env::from_raw(napi_env); | ||
let threadsafe_fn = ThreadsafeFunction::from_napi_value(napi_env, napi_val)?; | ||
Self::from_threadsafe_function(threadsafe_fn, env) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
pub(crate) use crate::conversion::{mysql, postgres, sqlite, JSArg}; | ||
|
||
use napi::bindgen_prelude::{FromNapiValue, ToNapiValue}; | ||
use napi::NapiValue; | ||
|
||
// FromNapiValue is the napi equivalent to serde::Deserialize. | ||
// Note: we can safely leave this unimplemented as we don't need deserialize napi_value back to JSArg. | ||
// However, removing this altogether would cause a compile error. | ||
impl FromNapiValue for JSArg { | ||
unsafe fn from_napi_value(_env: napi::sys::napi_env, _napi_value: napi::sys::napi_value) -> napi::Result<Self> { | ||
unreachable!() | ||
} | ||
} | ||
|
||
// ToNapiValue is the napi equivalent to serde::Serialize. | ||
impl ToNapiValue for JSArg { | ||
unsafe fn to_napi_value(env: napi::sys::napi_env, value: Self) -> napi::Result<napi::sys::napi_value> { | ||
match value { | ||
JSArg::Value(v) => ToNapiValue::to_napi_value(env, v), | ||
JSArg::Buffer(bytes) => { | ||
ToNapiValue::to_napi_value(env, napi::Env::from_raw(env).create_buffer_with_data(bytes)?.into_raw()) | ||
} | ||
// While arrays are encodable as JSON generally, their element might not be, or may be | ||
// represented in a different way than we need. We use this custom logic for all arrays | ||
// to avoid having separate `JsonArray` and `BytesArray` variants in `JSArg` and | ||
// avoid complicating the logic in `conv_params`. | ||
JSArg::Array(items) => { | ||
let env = napi::Env::from_raw(env); | ||
let mut array = env.create_array(items.len().try_into().expect("JS array length must fit into u32"))?; | ||
|
||
for (index, item) in items.into_iter().enumerate() { | ||
let js_value = ToNapiValue::to_napi_value(env.raw(), item)?; | ||
// TODO: NapiRaw could be implemented for sys::napi_value directly, there should | ||
// be no need for re-wrapping; submit a patch to napi-rs and simplify here. | ||
array.set(index as u32, napi::JsUnknown::from_raw_unchecked(env.raw(), js_value))?; | ||
} | ||
|
||
ToNapiValue::to_napi_value(env.raw(), array) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
use futures::{Future, FutureExt}; | ||
use napi::Error as NapiError; | ||
use quaint::error::Error as QuaintError; | ||
use std::{any::Any, panic::AssertUnwindSafe}; | ||
|
||
/// transforms a napi error into a quaint error copying the status and reason | ||
/// properties over | ||
pub(crate) fn into_quaint_error(napi_err: NapiError) -> QuaintError { | ||
let status = napi_err.status.as_ref().to_owned(); | ||
let reason = napi_err.reason.clone(); | ||
|
||
QuaintError::raw_connector_error(status, reason) | ||
} | ||
|
||
/// catches a panic thrown during the execution of an asynchronous closure and transforms it into | ||
/// the Error variant of a napi::Result. | ||
pub(crate) async fn async_unwinding_panic<F, R>(fut: F) -> napi::Result<R> | ||
where | ||
F: Future<Output = napi::Result<R>>, | ||
{ | ||
AssertUnwindSafe(fut) | ||
.catch_unwind() | ||
.await | ||
.unwrap_or_else(panic_to_napi_err) | ||
} | ||
|
||
fn panic_to_napi_err<R>(panic_payload: Box<dyn Any + Send>) -> napi::Result<R> { | ||
panic_payload | ||
.downcast_ref::<&str>() | ||
.map(|s| -> String { (*s).to_owned() }) | ||
.or_else(|| panic_payload.downcast_ref::<String>().map(|s| s.to_owned())) | ||
.map(|message| Err(napi::Error::from_reason(format!("PANIC: {message}")))) | ||
.ok_or(napi::Error::from_reason("PANIC: unknown panic".to_string())) | ||
.unwrap() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
//! Query Engine Driver Adapters: `napi`-specific implementation. | ||
mod async_js_function; | ||
mod conversion; | ||
mod error; | ||
mod proxy; | ||
mod queryable; | ||
mod result; | ||
mod transaction; | ||
pub use queryable::{from_napi, JsQueryable}; |
Oops, something went wrong.