feat(napi): implement serde feature
This commit is contained in:
parent
d0eadec77b
commit
5574930e18
3 changed files with 145 additions and 40 deletions
|
@ -10,6 +10,14 @@ keywords = ["NodeJS", "FFI"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
futures = { version = "0.3", features = ["default", "thread-pool"] }
|
futures = { version = "0.3", features = ["default", "thread-pool"] }
|
||||||
|
serde = { version = "1", optional = true }
|
||||||
|
serde_json = { version = "1", optional = true }
|
||||||
|
rayon = { version = "1", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
serde-json = ["serde", "serde_json", "rayon"]
|
||||||
|
|
||||||
|
default = ["serde-json"]
|
||||||
|
|
||||||
[target.'cfg(windows)'.build-dependencies]
|
[target.'cfg(windows)'.build-dependencies]
|
||||||
flate2 = "1.0"
|
flate2 = "1.0"
|
||||||
|
|
169
napi/src/lib.rs
169
napi/src/lib.rs
|
@ -1,3 +1,8 @@
|
||||||
|
#[cfg(feature = "serde-json")]
|
||||||
|
extern crate rayon;
|
||||||
|
#[cfg(feature = "serde-json")]
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
pub extern crate futures;
|
pub extern crate futures;
|
||||||
use std::any::TypeId;
|
use std::any::TypeId;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
@ -9,6 +14,9 @@ use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
use std::string::String as RustString;
|
use std::string::String as RustString;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde-json")]
|
||||||
|
use serde_json::value::Value as SerdeValue;
|
||||||
|
|
||||||
mod executor;
|
mod executor;
|
||||||
pub mod sys;
|
pub mod sys;
|
||||||
|
|
||||||
|
@ -32,14 +40,18 @@ pub struct Any;
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Undefined;
|
pub struct Undefined;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct Null;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Boolean {
|
pub struct Boolean {
|
||||||
value: bool,
|
value: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Number {
|
pub enum Number {
|
||||||
int: i64,
|
Int(i64),
|
||||||
|
Double(f64),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
@ -188,7 +200,7 @@ macro_rules! callback {
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(Some(result)) => result.into_raw(),
|
Ok(Some(result)) => result.into_raw(),
|
||||||
Ok(None) => env.get_undefined().into_raw(),
|
Ok(None) => env.get_undefined().unwrap().into_raw(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if !cfg!(windows) {
|
if !cfg!(windows) {
|
||||||
let _ = writeln!(::std::io::stderr(), "Error calling function: {:?}", e);
|
let _ = writeln!(::std::io::stderr(), "Error calling function: {:?}", e);
|
||||||
|
@ -197,7 +209,7 @@ macro_rules! callback {
|
||||||
unsafe {
|
unsafe {
|
||||||
$crate::sys::napi_throw_error(raw_env, ptr::null(), message.as_ptr() as *const c_char);
|
$crate::sys::napi_throw_error(raw_env, ptr::null(), message.as_ptr() as *const c_char);
|
||||||
}
|
}
|
||||||
env.get_undefined().into_raw()
|
env.get_undefined().unwrap().into_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,11 +237,18 @@ impl Env {
|
||||||
Env(env)
|
Env(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_undefined<'a>(&'a self) -> Value<'a, Undefined> {
|
pub fn get_undefined<'a>(&'a self) -> Result<Value<'a, Undefined>> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let status = unsafe { sys::napi_get_undefined(self.0, &mut raw_value) };
|
let status = unsafe { sys::napi_get_undefined(self.0, &mut raw_value) };
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
check_status(status)?;
|
||||||
Value::from_raw_value(self, raw_value, Undefined)
|
Ok(Value::from_raw_value(self, raw_value, Undefined))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_null<'a>(&'a self) -> Result<Value<'a, Null>> {
|
||||||
|
let mut raw_value = ptr::null_mut();
|
||||||
|
let status = unsafe { sys::napi_get_null(self.0, &mut raw_value) };
|
||||||
|
check_status(status)?;
|
||||||
|
Ok(Value::from_raw_value(self, raw_value, Null))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_boolean(&self, value: bool) -> Value<Boolean> {
|
pub fn get_boolean(&self, value: bool) -> Value<Boolean> {
|
||||||
|
@ -239,15 +258,27 @@ impl Env {
|
||||||
Value::from_raw_value(self, raw_value, Boolean { value })
|
Value::from_raw_value(self, raw_value, Boolean { value })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_int64<'a>(&'a self, int: i64) -> Value<'a, Number> {
|
pub fn create_int64<'a>(&'a self, int: i64) -> Result<Value<'a, Number>> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let status =
|
let status =
|
||||||
unsafe { sys::napi_create_int64(self.0, int, (&mut raw_value) as *mut sys::napi_value) };
|
unsafe { sys::napi_create_int64(self.0, int, (&mut raw_value) as *mut sys::napi_value) };
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
check_status(status)?;
|
||||||
Value::from_raw_value(self, raw_value, Number { int })
|
Ok(Value::from_raw_value(self, raw_value, Number::Int(int)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_string<'a, 'b>(&'a self, s: &'b str) -> Value<'a, String> {
|
pub fn create_double<'a>(&'a self, double: f64) -> Result<Value<'a, Number>> {
|
||||||
|
let mut raw_value = ptr::null_mut();
|
||||||
|
let status =
|
||||||
|
unsafe { sys::napi_create_double(self.0, double, (&mut raw_value) as *mut sys::napi_value) };
|
||||||
|
check_status(status)?;
|
||||||
|
Ok(Value::from_raw_value(
|
||||||
|
self,
|
||||||
|
raw_value,
|
||||||
|
Number::Double(double),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_string<'a, 'b>(&'a self, s: &'b str) -> Result<Value<'a, String>> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
sys::napi_create_string_utf8(
|
sys::napi_create_string_utf8(
|
||||||
|
@ -257,8 +288,8 @@ impl Env {
|
||||||
&mut raw_value,
|
&mut raw_value,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
check_status(status)?;
|
||||||
Value::from_raw_value(self, raw_value, String)
|
Ok(Value::from_raw_value(self, raw_value, String))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_string_utf16<'a, 'b>(&'a self, chars: &[u16]) -> Value<'a, String> {
|
pub fn create_string_utf16<'a, 'b>(&'a self, chars: &[u16]) -> Value<'a, String> {
|
||||||
|
@ -277,6 +308,48 @@ impl Env {
|
||||||
Value::from_raw_value(self, raw_value, Object)
|
Value::from_raw_value(self, raw_value, Object)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde-json")]
|
||||||
|
pub fn from_serde_value<'a>(&'a self, serde_value: &'a SerdeValue) -> Result<Value<'a, Any>> {
|
||||||
|
match serde_value {
|
||||||
|
SerdeValue::Array(v) => {
|
||||||
|
let result = self.create_array_with_length(v.len());
|
||||||
|
v.iter().enumerate().try_for_each(|(index, value)| unsafe {
|
||||||
|
let status = sys::napi_set_element(
|
||||||
|
self.0,
|
||||||
|
result.into_raw(),
|
||||||
|
index as u32,
|
||||||
|
self.from_serde_value(value)?.into_raw(),
|
||||||
|
);
|
||||||
|
check_status(status)
|
||||||
|
})?;
|
||||||
|
Ok(result.into_any())
|
||||||
|
}
|
||||||
|
SerdeValue::Bool(v) => Ok(self.get_boolean(*v).into_any()),
|
||||||
|
SerdeValue::Number(v) => {
|
||||||
|
if v.is_f64() {
|
||||||
|
self
|
||||||
|
.create_double(v.as_f64().unwrap())
|
||||||
|
.map(|v| v.into_any())
|
||||||
|
} else if v.is_i64() {
|
||||||
|
self.create_int64(v.as_i64().unwrap()).map(|v| v.into_any())
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
.create_int64(v.as_u64().unwrap() as i64)
|
||||||
|
.map(|v| v.into_any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SerdeValue::Null => self.get_null().map(|v| v.into_any()),
|
||||||
|
SerdeValue::String(s) => self.create_string(s).map(|v| v.into_any()),
|
||||||
|
SerdeValue::Object(map) => {
|
||||||
|
let mut result = self.create_object();
|
||||||
|
map.iter().try_for_each(|(key, value)| {
|
||||||
|
result.set_named_property(key, self.from_serde_value(value)?)
|
||||||
|
})?;
|
||||||
|
Ok(result.into_any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_array_with_length(&self, length: usize) -> Value<Object> {
|
pub fn create_array_with_length(&self, length: usize) -> Value<Object> {
|
||||||
let mut raw_value = ptr::null_mut();
|
let mut raw_value = ptr::null_mut();
|
||||||
let status =
|
let status =
|
||||||
|
@ -381,12 +454,12 @@ impl Env {
|
||||||
name: &'b str,
|
name: &'b str,
|
||||||
constructor_cb: Callback,
|
constructor_cb: Callback,
|
||||||
properties: Vec<Property>,
|
properties: Vec<Property>,
|
||||||
) -> Value<'a, Function> {
|
) -> Result<Value<'a, Function>> {
|
||||||
let mut raw_result = ptr::null_mut();
|
let mut raw_result = ptr::null_mut();
|
||||||
let raw_properties = properties
|
let raw_properties = properties
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|prop| prop.into_raw(self))
|
.map(|prop| prop.into_raw(self))
|
||||||
.collect::<Vec<sys::napi_property_descriptor>>();
|
.collect::<Result<Vec<sys::napi_property_descriptor>>>()?;
|
||||||
|
|
||||||
let status = unsafe {
|
let status = unsafe {
|
||||||
sys::napi_define_class(
|
sys::napi_define_class(
|
||||||
|
@ -401,9 +474,9 @@ impl Env {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
check_status(status)?;
|
||||||
|
|
||||||
Value::from_raw_value(self, raw_result, Function)
|
Ok(Value::from_raw_value(self, raw_result, Function))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap<T: 'static>(&self, js_object: &mut Value<Object>, native_object: T) -> Result<()> {
|
pub fn wrap<T: 'static>(&self, js_object: &mut Value<Object>, native_object: T) -> Result<()> {
|
||||||
|
@ -460,46 +533,48 @@ impl Env {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn async_init(&self, resource: Option<Value<Object>>, name: &str) -> AsyncContext {
|
pub fn async_init(&self, resource: Option<Value<Object>>, name: &str) -> Result<AsyncContext> {
|
||||||
let raw_resource = resource
|
let raw_resource = resource
|
||||||
.map(|r| r.into_raw())
|
.map(|r| r.into_raw())
|
||||||
.unwrap_or_else(|| self.create_object().into_raw());
|
.unwrap_or_else(|| self.create_object().into_raw());
|
||||||
let raw_name = self.create_string(name).into_raw();
|
let raw_name = self.create_string(name)?.into_raw();
|
||||||
|
|
||||||
let mut raw_context = ptr::null_mut();
|
let mut raw_context = ptr::null_mut();
|
||||||
let mut raw_resource_ref = ptr::null_mut();
|
let mut raw_resource_ref = ptr::null_mut();
|
||||||
unsafe {
|
unsafe {
|
||||||
let status = sys::napi_async_init(self.0, raw_resource, raw_name, &mut raw_context);
|
let status = sys::napi_async_init(self.0, raw_resource, raw_name, &mut raw_context);
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
check_status(status)?;
|
||||||
|
|
||||||
let status = sys::napi_create_reference(self.0, raw_resource, 1, &mut raw_resource_ref);
|
let status = sys::napi_create_reference(self.0, raw_resource, 1, &mut raw_resource_ref);
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
check_status(status)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncContext {
|
Ok(AsyncContext {
|
||||||
raw_env: self.0,
|
raw_env: self.0,
|
||||||
raw_resource: raw_resource_ref,
|
raw_resource: raw_resource_ref,
|
||||||
raw_context,
|
raw_context,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_promise(&self) -> (Value<Object>, Deferred) {
|
pub fn create_promise(&self) -> Result<(Value<Object>, Deferred)> {
|
||||||
let mut raw_promise = ptr::null_mut();
|
let mut raw_promise = ptr::null_mut();
|
||||||
let mut raw_deferred = ptr::null_mut();
|
let mut raw_deferred = ptr::null_mut();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::napi_create_promise(self.0, &mut raw_deferred, &mut raw_promise);
|
let status = sys::napi_create_promise(self.0, &mut raw_deferred, &mut raw_promise);
|
||||||
|
check_status(status)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
Ok((
|
||||||
Value::from_raw_value(self, raw_promise, Object),
|
Value::from_raw_value(self, raw_promise, Object),
|
||||||
Deferred(raw_deferred),
|
Deferred(raw_deferred),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_deferred<T: ValueType>(&self, deferred: Deferred, value: Value<T>) {
|
pub fn resolve_deferred<T: ValueType>(&self, deferred: Deferred, value: Value<T>) -> Result<()> {
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::napi_resolve_deferred(self.0, deferred.0, value.into_raw());
|
let status = sys::napi_resolve_deferred(self.0, deferred.0, value.into_raw());
|
||||||
|
check_status(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -534,6 +609,16 @@ impl ValueType for Undefined {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ValueType for Null {
|
||||||
|
fn from_raw(_env: sys::napi_env, _raw: sys::napi_value) -> Self {
|
||||||
|
Null
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool {
|
||||||
|
get_raw_type(env, raw) == sys::napi_valuetype::napi_null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ValueType for Boolean {
|
impl ValueType for Boolean {
|
||||||
fn from_raw(env: sys::napi_env, raw: sys::napi_value) -> Self {
|
fn from_raw(env: sys::napi_env, raw: sys::napi_value) -> Self {
|
||||||
let mut value = true;
|
let mut value = true;
|
||||||
|
@ -549,10 +634,10 @@ impl ValueType for Boolean {
|
||||||
|
|
||||||
impl ValueType for Number {
|
impl ValueType for Number {
|
||||||
fn from_raw(env: sys::napi_env, raw: sys::napi_value) -> Self {
|
fn from_raw(env: sys::napi_env, raw: sys::napi_value) -> Self {
|
||||||
let mut int: i64 = 0;
|
let mut double: f64 = 0.0;
|
||||||
let status = unsafe { sys::napi_get_value_int64(env, raw, &mut int) };
|
let status = unsafe { sys::napi_get_value_double(env, raw, &mut double) };
|
||||||
debug_assert!(Status::from(status) == Status::Ok);
|
debug_assert!(Status::from(status) == Status::Ok);
|
||||||
Number { int }
|
Number::Double(double)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool {
|
fn matches_raw_type(env: sys::napi_env, raw: sys::napi_value) -> bool {
|
||||||
|
@ -694,6 +779,15 @@ impl<'env, T: ValueType> Value<'env, T> {
|
||||||
value: Object,
|
value: Object,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_any(self) -> Value<'env, Any> {
|
||||||
|
Value {
|
||||||
|
env: self.env,
|
||||||
|
raw_value: self.raw_value,
|
||||||
|
value: Any,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -851,7 +945,7 @@ impl<'env> Value<'env, Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_index<'a, T>(&mut self, index: usize, value: Value<T>) -> Result<()> {
|
pub fn set_index<'a, T>(&mut self, index: usize, value: Value<T>) -> Result<()> {
|
||||||
self.set_property(self.env.create_int64(index as i64), value)
|
self.set_property(self.env.create_int64(index as i64)?, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_index<T: ValueType>(&self, index: u32) -> Result<Value<T>> {
|
pub fn get_index<T: ValueType>(&self, index: u32) -> Result<Value<T>> {
|
||||||
|
@ -929,7 +1023,8 @@ impl<'env> Value<'env, Function> {
|
||||||
) -> Result<Value<'env, Any>> {
|
) -> Result<Value<'env, Any>> {
|
||||||
let raw_this = this
|
let raw_this = this
|
||||||
.map(|v| v.into_raw())
|
.map(|v| v.into_raw())
|
||||||
.unwrap_or_else(|| self.env.get_undefined().into_raw());
|
.or_else(|| self.env.get_undefined().ok().map(|u| u.into_raw()))
|
||||||
|
.ok_or(Error::new(Status::Unknown))?;
|
||||||
let mut raw_args = unsafe { mem::MaybeUninit::<[sys::napi_value; 8]>::uninit().assume_init() };
|
let mut raw_args = unsafe { mem::MaybeUninit::<[sys::napi_value; 8]>::uninit().assume_init() };
|
||||||
for (i, arg) in args.into_iter().enumerate() {
|
for (i, arg) in args.into_iter().enumerate() {
|
||||||
raw_args[i] = arg.raw_value;
|
raw_args[i] = arg.raw_value;
|
||||||
|
@ -1032,9 +1127,9 @@ impl Property {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_raw(mut self, env: &Env) -> sys::napi_property_descriptor {
|
fn into_raw(mut self, env: &Env) -> Result<sys::napi_property_descriptor> {
|
||||||
self.raw_descriptor.name = env.create_string(&self.name).into_raw();
|
self.raw_descriptor.name = env.create_string(&self.name)?.into_raw();
|
||||||
self.raw_descriptor
|
Ok(self.raw_descriptor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,9 +28,9 @@ fn test_spawn<'a>(
|
||||||
use futures::executor::ThreadPool;
|
use futures::executor::ThreadPool;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
|
||||||
let async_context = env.async_init(None, "test_spawn");
|
let async_context = env.async_init(None, "test_spawn")?;
|
||||||
let pool = ThreadPool::new().expect("Failed to build pool");
|
let pool = ThreadPool::new().expect("Failed to build pool");
|
||||||
let (promise, deferred) = env.create_promise();
|
let (promise, deferred) = env.create_promise()?;
|
||||||
let (tx, rx) = futures::channel::mpsc::unbounded::<i32>();
|
let (tx, rx) = futures::channel::mpsc::unbounded::<i32>();
|
||||||
let fut_values = async move {
|
let fut_values = async move {
|
||||||
let fut_tx_result = async move {
|
let fut_tx_result = async move {
|
||||||
|
@ -45,7 +45,9 @@ fn test_spawn<'a>(
|
||||||
println!("Collected result lenght {}", results.len());
|
println!("Collected result lenght {}", results.len());
|
||||||
};
|
};
|
||||||
async_context.enter(|env| {
|
async_context.enter(|env| {
|
||||||
env.resolve_deferred(deferred, env.get_undefined());
|
env
|
||||||
|
.resolve_deferred(deferred, env.get_undefined().unwrap())
|
||||||
|
.unwrap();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue