Compare commits

...

2 Commits

Author SHA1 Message Date
K.J. Valencik
4ad2e5ff06
feat(neon): Initial serde implementation 2021-03-18 13:01:38 -04:00
K.J. Valencik
c105c159e8
fix(neon-runtime): Add #[must_use] to napi::Status and fix places it was being ignored 2021-03-18 13:01:34 -04:00
23 changed files with 6474 additions and 9 deletions

View File

@ -2,4 +2,4 @@
# Neon defines mutually exclusive feature flags which prevents using `cargo clippy --all-features`
# The following aliases simplify linting the entire workspace
clippy-legacy = "clippy --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p tests -p static_tests --features event-handler-api,proc-macros,try-catch-api,legacy-runtime -- -A clippy::missing_safety_doc"
clippy-napi = "clippy --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p electron-tests -p napi-tests --features proc-macros,try-catch-api,napi-experimental -- -A clippy::missing_safety_doc"
clippy-napi = "clippy --all-targets --no-default-features -p neon -p neon-runtime -p neon-build -p neon-macros -p electron-tests -p napi-tests --features proc-macros,try-catch-api,napi-experimental,serde -- -A clippy::missing_safety_doc"

View File

@ -24,6 +24,7 @@ semver = "0.9.0"
smallvec = "1.4.2"
neon-runtime = { version = "=0.7.1", path = "crates/neon-runtime" }
neon-macros = { version = "=0.7.1", path = "crates/neon-macros", optional = true }
serde_crate = { package = "serde", version = "1", optional = true }
[features]
default = ["legacy-runtime"]
@ -72,6 +73,9 @@ event-queue-api = []
# Feature flag to include procedural macros
proc-macros = ["neon-macros"]
# Feature flag to enable serde on Neon types
serde = ["serde_crate", "neon-runtime/serde"]
[package.metadata.docs.rs]
features = ["docs-only", "event-handler-api", "proc-macros", "try-catch-api"]

View File

@ -9,8 +9,10 @@ edition = "2018"
[dependencies]
cfg-if = "1.0.0"
conv = { version = "0.3", optional = true }
libloading = { version = "0.6.5", optional = true }
neon-sys = { version = "=0.7.1", path = "../neon-sys", optional = true }
serde_crate = { package = "serde", version = "1", optional = true }
smallvec = "1.4.2"
[dev-dependencies]
@ -25,6 +27,7 @@ napi-4 = ["napi-3"]
napi-5 = ["napi-4"]
napi-6 = ["napi-5"]
napi-experimental = ["napi-6"]
serde = ["serde_crate", "conv"]
docs-only = ["neon-sys/docs-only"]
[package.metadata.docs.rs]

View File

@ -200,6 +200,17 @@ mod napi1 {
);
}
#[cfg(feature = "serde")]
mod napi1_serde {
use super::super::types::*;
generate!(
extern "C" {
fn get_property_names(env: Env, object: Value, result: *mut Value) -> Status;
}
);
}
#[cfg(feature = "napi-4")]
mod napi4 {
use super::super::types::*;
@ -273,6 +284,8 @@ mod napi6 {
}
pub(crate) use napi1::*;
#[cfg(feature = "serde")]
pub(crate) use napi1_serde::*;
#[cfg(feature = "napi-4")]
pub(crate) use napi4::*;
#[cfg(feature = "napi-5")]
@ -304,6 +317,9 @@ pub(crate) unsafe fn load(env: Env) -> Result<(), libloading::Error> {
napi1::load(&host, version, 1)?;
#[cfg(feature = "serde")]
napi1_serde::load(&host, version, 1)?;
#[cfg(feature = "napi-4")]
napi4::load(&host, version, 4)?;

View File

@ -69,6 +69,7 @@ pub type ThreadsafeFunctionCallJs = Option<
#[allow(dead_code)]
#[repr(u32)]
#[must_use]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Status {
Ok = 0,

View File

@ -19,5 +19,8 @@ pub mod tag;
#[cfg(feature = "napi-4")]
pub mod tsfn;
#[cfg(feature = "serde")]
pub mod serde;
mod bindings;
pub use bindings::*;

View File

@ -5,7 +5,7 @@ use crate::raw::{Env, Local};
/// Mutates the `out` argument to refer to a `napi_value` containing a newly created JavaScript Object.
pub unsafe fn new(out: &mut Local, env: Env) {
napi::create_object(env, out as *mut _);
assert_eq!(napi::create_object(env, out as *mut _), napi::Status::Ok,);
}
#[cfg(feature = "napi-6")]

View File

@ -3,17 +3,23 @@ use crate::raw::{Env, Local};
/// Mutates the `out` argument provided to refer to the global `undefined` object.
pub unsafe fn undefined(out: &mut Local, env: Env) {
napi::get_undefined(env, out as *mut Local);
assert_eq!(
napi::get_undefined(env, out as *mut Local),
napi::Status::Ok,
);
}
/// Mutates the `out` argument provided to refer to the global `null` object.
pub unsafe fn null(out: &mut Local, env: Env) {
napi::get_null(env, out as *mut Local);
assert_eq!(napi::get_null(env, out as *mut Local), napi::Status::Ok,);
}
/// Mutates the `out` argument provided to refer to one of the global `true` or `false` objects.
pub unsafe fn boolean(out: &mut Local, env: Env, b: bool) {
napi::get_boolean(env, b, out as *mut Local);
assert_eq!(
napi::get_boolean(env, b, out as *mut Local),
napi::Status::Ok,
);
}
/// Get the boolean value out of a `Local` object. If the `Local` object does not contain a
@ -30,7 +36,10 @@ pub unsafe fn boolean_value(env: Env, p: Local) -> bool {
/// Mutates the `out` argument provided to refer to a newly created `Local` containing a
/// JavaScript number.
pub unsafe fn number(out: &mut Local, env: Env, v: f64) {
napi::create_double(env, v, out as *mut Local);
assert_eq!(
napi::create_double(env, v, out as *mut Local),
napi::Status::Ok,
);
}
/// Gets the underlying value of an `Local` object containing a JavaScript number. Panics if

View File

@ -0,0 +1,484 @@
//! Implements a serde deserializer for JavaScript values
//!
//! # Safety
//!
//! All JavaScript types are neither `Send` or `Sync`. Threads should be used.
use conv::{ApproxFrom, DefaultApprox};
use serde_crate::de::{
self, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess, VariantAccess,
Visitor,
};
use super::*;
use crate::napi;
/// High level deserializer for all JavaScript values
pub(super) struct Deserializer {
env: napi::Env,
value: napi::Value,
}
impl Deserializer {
pub(super) fn new(env: napi::Env, value: napi::Value) -> Self {
Deserializer { env, value }
}
}
/// Specialized deserializer for `Array`
struct ArrayAccessor {
env: napi::Env,
array: napi::Value,
len: u32,
index: u32,
}
impl ArrayAccessor {
fn new(env: napi::Env, array: napi::Value) -> Result<Self, Error> {
Ok(Self {
env,
array,
len: get_array_len(env, array)?,
index: 0,
})
}
fn next(&mut self) -> Result<Option<napi::Value>, Error> {
if self.index >= self.len {
return Ok(None);
}
let element = get_array_element(self.env, self.array, self.index)?;
self.index += 1;
Ok(Some(element))
}
}
/// Specialized deserializer for generic `Object`
/// Only enumerable keys are read
struct ObjectAccessor {
env: napi::Env,
object: napi::Value,
keys: ArrayAccessor,
// Cache the most recent key for reading the next value
next: Option<napi::Value>,
}
impl ObjectAccessor {
fn new(env: napi::Env, object: napi::Value) -> Result<Self, Error> {
let keys = get_property_names(env, object)?;
let keys = ArrayAccessor::new(env, keys)?;
Ok(Self {
env,
object,
keys,
next: None,
})
}
}
impl de::Deserializer<'static> for Deserializer {
type Error = Error;
// JavaScript is a self describing format, allowing us to provide a deserialization
// implementation without prior knowledge of the schema. This is useful for types
// like `serde_json::Value`.
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
match typeof_value(self.env, self.value)? {
napi::ValueType::Undefined | napi::ValueType::Null => self.deserialize_unit(visitor),
napi::ValueType::Boolean => self.deserialize_bool(visitor),
napi::ValueType::Number => self.deserialize_f64(visitor),
napi::ValueType::String => self.deserialize_string(visitor),
napi::ValueType::Object => self.deserialize_map(visitor),
typ => Err(Error::unsupported_type(typ)),
}
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_bool(get_value_bool(self.env, self.value)?)
}
// JavaScript only provides an `f64` number type. All integer types require
// a lossy approximation. Out of range values will error.
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <i8 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_i8(n)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <i16 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_i16(n)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <i32 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_i32(n)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <i64 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_i64(n)
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <u8 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_u8(n)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <u16 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_u16(n)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <u32 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_u32(n)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <u64 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_u64(n)
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
let n = <f32 as ApproxFrom<_, DefaultApprox>>::approx_from(n)?;
visitor.visit_f32(n)
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
let n = get_value_double(self.env, self.value)?;
visitor.visit_f64(n)
}
// `char` are serialized as a single character `string`
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_string(visitor)
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_string(visitor)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_string(get_value_string(self.env, self.value)?)
}
// This could be optimized to borrow the bytes from the JavaScript value
// However, since JavaScript values are neither `Send` or `Sync` it is not
// generally useful
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_byte_buf(visitor)
}
// Bytes are serialized as the idiomatic `ArrayBuffer` JavaScript type
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_byte_buf(get_value_arraybuffer(self.env, self.value)?)
}
// `None` are serialized as `null`, but when deserializing `undefined` is
// also accepted.
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
match typeof_value(self.env, self.value)? {
napi::ValueType::Null | napi::ValueType::Undefined => visitor.visit_none(),
_ => visitor.visit_some(self),
}
}
// JavaScript does not have a concept of unit; `null` or `undefined` is accepted
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
match typeof_value(self.env, self.value)? {
napi::ValueType::Null | napi::ValueType::Undefined => visitor.visit_unit(),
_ => Err(Error::expected_null()),
}
}
fn deserialize_unit_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_unit(visitor)
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_newtype_struct(self)
}
// `Array` is used since it is the only sequence type in JavaScript
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_seq(ArrayAccessor::new(self.env, self.value)?)
}
// `Array` are used to serialize tuples; this is a common pattern, especially in TypeScript
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_seq(visitor)
}
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_seq(visitor)
}
// Generic `Object` are used to serialize map
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_map(ObjectAccessor::new(self.env, self.value)?)
}
fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_map(visitor)
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
// No-value enums are serialized as `string`
if typeof_value(self.env, self.value)? == napi::ValueType::String {
let s = get_value_string(self.env, self.value)?;
visitor.visit_enum(s.into_deserializer())
} else {
visitor.visit_enum(self)
}
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
self.deserialize_string(visitor)
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
visitor.visit_unit()
}
}
impl SeqAccess<'static> for ArrayAccessor {
type Error = Error;
// This will have unpredictable results if the `Array` has a getter that mutates
// the object. It should be _safe_ and return an `Error`, but hopefully users
// don't do this.
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: DeserializeSeed<'static>,
{
self.next()?
.map(|v| seed.deserialize(Deserializer::new(self.env, v)))
.transpose()
}
// We can efficiently provide a size hint since `Array` have known length
fn size_hint(&self) -> Option<usize> {
Some((self.len - self.index) as usize)
}
}
impl MapAccess<'static> for ObjectAccessor {
type Error = Error;
// This will have unpredictable results if the `Object` has a getter that mutates
// the object. It should be _safe_ and return an `Error`, but hopefully users
// don't do this on serializable types.
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: DeserializeSeed<'static>,
{
// Store the next `key` for deserializing the value in `next_value_seed`
self.next = self.keys.next()?;
self.next
.map(|v| seed.deserialize(Deserializer::new(self.env, v)))
.transpose()
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: DeserializeSeed<'static>,
{
// `Error::missing_key` should only happen in a buggy serde implementation
let key = self.next.ok_or_else(Error::missing_key)?;
let value = get_property(self.env, self.object, key)?;
seed.deserialize(Deserializer::new(self.env, value))
}
// We can efficiently provide a size hint since we fetch all keys ahead of time
fn size_hint(&self) -> Option<usize> {
self.keys.size_hint()
}
}
impl EnumAccess<'static> for Deserializer {
type Error = Error;
type Variant = Self;
// Enums are serialized as `{ [type]: value }`
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: DeserializeSeed<'static>,
{
let keys = get_property_names(self.env, self.value)?;
let key = get_array_element(self.env, keys, 0)?;
let value = get_property(self.env, self.value, key)?;
let deserializer = Deserializer::new(self.env, value);
let key = seed.deserialize(self)?;
Ok((key, deserializer))
}
}
// Externally tagged enum can be treated equivalent to the enclosed type
impl VariantAccess<'static> for Deserializer {
type Error = Error;
fn unit_variant(self) -> Result<(), Self::Error> {
Err(Error::expected_string())
}
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
where
T: DeserializeSeed<'static>,
{
seed.deserialize(self)
}
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
de::Deserializer::deserialize_seq(self, visitor)
}
fn struct_variant<V>(
self,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: Visitor<'static>,
{
de::Deserializer::deserialize_map(self, visitor)
}
}

View File

@ -0,0 +1,149 @@
//! Error type and conversions for serde transcoding
use std::error;
use std::fmt;
use conv::{FloatError, PosOverflow, RangeError};
use serde_crate::{de, ser};
use crate::napi;
#[derive(Clone, Debug, PartialEq)]
/// This type represents all possible errors that can occur when serializing or
/// deserializing JavaScript types.
pub struct Error {
kind: ErrorKind,
}
impl error::Error for Error {}
impl Error {
fn new(kind: ErrorKind) -> Self {
Self { kind }
}
/// Indicates if the error was due to an exception in the JavaScript VM
/// If an exception is pending, all other JavaScript operations will fail
/// until it is cleared.
pub fn is_exception_pending(&self) -> bool {
self.kind == ErrorKind::Napi(napi::Status::PendingException)
}
pub(super) fn expected_null() -> Self {
ErrorKind::ExpectedNull.into()
}
pub(super) fn expected_string() -> Self {
ErrorKind::ExpectedString.into()
}
pub(super) fn missing_key() -> Self {
ErrorKind::MissingKey.into()
}
pub(super) fn unsupported_type(typ: napi::ValueType) -> Self {
ErrorKind::UnsupportedType(typ).into()
}
}
#[derive(Clone, Debug, PartialEq)]
pub(super) enum ErrorKind {
// Serde
Custom(String),
// Serde reads and writes key/value pairs as distinct steps requiring
// Neon to cache the intermediate key. This error is unexpected and should
// never occur outside of a buggy serde implementation.
MissingKey,
// Number conversions
FloatError(FloatError<f64>),
I64Error(RangeError<i64>),
U64Error(PosOverflow<u64>),
UsizeError(PosOverflow<usize>),
// deserialize_any
ExpectedNull,
ExpectedString,
UnsupportedType(napi::ValueType),
// N-API
Napi(napi::Status),
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Self {
Error::new(kind)
}
}
impl From<FloatError<f64>> for Error {
fn from(other: FloatError<f64>) -> Self {
ErrorKind::FloatError(other).into()
}
}
impl From<RangeError<f64>> for Error {
fn from(other: RangeError<f64>) -> Self {
let err = match other {
RangeError::PosOverflow(v) => FloatError::PosOverflow(v),
RangeError::NegOverflow(v) => FloatError::NegOverflow(v),
};
err.into()
}
}
impl From<RangeError<i64>> for Error {
fn from(other: RangeError<i64>) -> Self {
ErrorKind::I64Error(other).into()
}
}
impl From<PosOverflow<u64>> for Error {
fn from(other: PosOverflow<u64>) -> Self {
ErrorKind::U64Error(other).into()
}
}
impl From<PosOverflow<usize>> for Error {
fn from(other: PosOverflow<usize>) -> Self {
ErrorKind::UsizeError(other).into()
}
}
impl From<napi::Status> for Error {
fn from(other: napi::Status) -> Self {
ErrorKind::Napi(other).into()
}
}
impl de::Error for Error {
fn custom<T: fmt::Display>(err: T) -> Self {
Error {
kind: ErrorKind::Custom(err.to_string()),
}
}
}
impl ser::Error for Error {
fn custom<T: fmt::Display>(err: T) -> Self {
de::Error::custom(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.kind {
ErrorKind::Custom(err) => f.write_str(err),
ErrorKind::MissingKey => f.write_str("MissingKey"),
ErrorKind::FloatError(err) => fmt::Display::fmt(err, f),
ErrorKind::I64Error(err) => fmt::Display::fmt(err, f),
ErrorKind::U64Error(err) => fmt::Display::fmt(err, f),
ErrorKind::UsizeError(err) => fmt::Display::fmt(err, f),
ErrorKind::ExpectedNull => f.write_str("ExpectedNull"),
ErrorKind::ExpectedString => f.write_str("ExpectedString"),
ErrorKind::UnsupportedType(typ) => write!(f, "UnsupportedType({:?})", typ),
ErrorKind::Napi(err) => write!(f, "Napi({:?})", err),
}
}
}

View File

@ -0,0 +1,282 @@
//! N-API wrappers used by serde transcoding
//!
//! In many cases, these functions provide similar functionality to functions
//! available elsewhere in `neon-runtime`. However, keeping serde fully self
//! contained has a few benefits:
//!
//! * Wrappers can be written, altered, combined and otherwise optimized for
//! providing the most efficient possible serde implementation
//! * All wrappers can be written idiomatically instead of matching the legacy
//! behavior of `neon-sys`
//! * The serde implementation remains self contained for potential extraction
//! into a separate crate
//!
//! # Safety
//!
//! _Do not export anything from this file outside of the serde module._
//!
//! Nearly all functions in this crate are `unsafe` and should be treated that
//! way despite being marked safe. However, since the serde implementation is
//! unsafe in it's entirety, these wrappers are marked safe to make the
//! implementation easier to read and review.
use std::mem::MaybeUninit;
use std::ptr;
use std::slice;
use crate::napi;
trait Verify {
fn verify(self) -> Result<(), napi::Status>;
}
impl Verify for napi::Status {
fn verify(self) -> Result<(), napi::Status> {
if self == napi::Status::Ok {
Ok(())
} else {
Err(self)
}
}
}
pub(super) fn get_value_bool(env: napi::Env, value: napi::Value) -> Result<bool, napi::Status> {
let mut out = false;
unsafe {
napi::get_value_bool(env, value, &mut out as *mut bool).verify()?;
};
Ok(out)
}
pub(super) fn get_value_double(env: napi::Env, value: napi::Value) -> Result<f64, napi::Status> {
let mut out = 0f64;
unsafe {
napi::get_value_double(env, value, &mut out as *mut f64).verify()?;
};
Ok(out)
}
pub(super) fn get_string_len(env: napi::Env, value: napi::Value) -> Result<usize, napi::Status> {
let mut out = 0usize;
unsafe {
napi::get_value_string_utf8(env, value, ptr::null_mut(), 0, &mut out as *mut usize)
.verify()?;
}
Ok(out)
}
pub(super) fn get_value_string(env: napi::Env, value: napi::Value) -> Result<String, napi::Status> {
let mut out = 0usize;
let string_len = get_string_len(env, value)?;
let buf_len = string_len + 1;
let mut buf = Vec::<u8>::with_capacity(buf_len);
unsafe {
napi::get_value_string_utf8(
env,
value,
buf.as_mut_ptr().cast(),
buf_len,
&mut out as *mut usize,
)
.verify()?;
debug_assert_eq!(out, string_len);
buf.set_len(string_len);
Ok(String::from_utf8_unchecked(buf))
}
}
pub(super) fn get_value_arraybuffer(
env: napi::Env,
value: napi::Value,
) -> Result<Vec<u8>, napi::Status> {
let mut len = 0usize;
let mut out = MaybeUninit::uninit();
unsafe {
napi::get_arraybuffer_info(env, value, out.as_mut_ptr(), &mut len as *mut usize)
.verify()?;
};
let buf = unsafe { slice::from_raw_parts(out.assume_init().cast(), len) };
Ok(buf.to_vec())
}
pub(super) fn get_array_len(env: napi::Env, value: napi::Value) -> Result<u32, napi::Status> {
let mut len = 0u32;
unsafe {
napi::get_array_length(env, value, &mut len as *mut u32).verify()?;
};
Ok(len)
}
pub(super) fn get_array_element(
env: napi::Env,
arr: napi::Value,
i: u32,
) -> Result<napi::Value, napi::Status> {
let mut out = MaybeUninit::uninit();
unsafe {
napi::get_element(env, arr, i, out.as_mut_ptr()).verify()?;
Ok(out.assume_init())
}
}
pub(super) fn typeof_value(
env: napi::Env,
value: napi::Value,
) -> Result<napi::ValueType, napi::Status> {
let mut out = MaybeUninit::uninit();
unsafe {
napi::typeof_value(env, value, out.as_mut_ptr()).verify()?;
Ok(out.assume_init())
}
}
pub(super) fn get_property_names(
env: napi::Env,
value: napi::Value,
) -> Result<napi::Value, napi::Status> {
let mut out = MaybeUninit::uninit();
unsafe {
napi::get_property_names(env, value, out.as_mut_ptr()).verify()?;
Ok(out.assume_init())
}
}
pub(super) fn get_property(
env: napi::Env,
object: napi::Value,
key: napi::Value,
) -> Result<napi::Value, napi::Status> {
let mut out = MaybeUninit::uninit();
unsafe {
napi::get_property(env, object, key, out.as_mut_ptr()).verify()?;
Ok(out.assume_init())
}
}
pub(super) fn get_null(env: napi::Env) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
unsafe {
napi::get_null(env, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_double(
env: napi::Env,
v: impl Into<f64>,
) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
let v = v.into();
unsafe {
napi::create_double(env, v, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_bool(env: napi::Env, v: bool) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
unsafe {
napi::get_boolean(env, v, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_string(
env: napi::Env,
v: impl AsRef<str>,
) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
let v = v.as_ref();
unsafe {
napi::create_string_utf8(env, v.as_ptr().cast(), v.len(), value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_object(env: napi::Env) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
unsafe {
napi::create_object(env, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_array_with_length(
env: napi::Env,
len: usize,
) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
unsafe {
napi::create_array_with_length(env, len, value.as_mut_ptr()).verify()?;
Ok(value.assume_init())
}
}
pub(super) fn create_arraybuffer(env: napi::Env, v: &[u8]) -> Result<napi::Value, napi::Status> {
let mut value = MaybeUninit::uninit();
let mut data = MaybeUninit::uninit();
unsafe {
napi::create_arraybuffer(env, v.len(), data.as_mut_ptr(), value.as_mut_ptr()).verify()?;
};
let data = unsafe {
let data = data.assume_init().cast();
std::slice::from_raw_parts_mut(data, v.len())
};
data.copy_from_slice(v);
Ok(unsafe { value.assume_init() })
}
pub(super) fn object_set(
env: napi::Env,
o: napi::Value,
k: napi::Value,
v: napi::Value,
) -> Result<(), napi::Status> {
unsafe {
napi::set_property(env, o, k, v).verify()?;
};
Ok(())
}
pub(super) fn array_set(
env: napi::Env,
arr: napi::Value,
k: u32,
v: napi::Value,
) -> Result<(), napi::Status> {
unsafe {
napi::set_element(env, arr, k, v).verify()?;
}
Ok(())
}

View File

@ -0,0 +1,35 @@
//! Serde implementation for converting between Rust and JavaScript data types
mod de;
mod error;
mod js;
mod se;
use serde_crate::{Deserialize, Serialize};
use crate::napi;
pub use self::error::Error;
use self::js::*;
/// Attempts to read a JavaScript value into a Rust data type using the serde::Deserialize implementation
/// # Safety
/// * `env` must point to the JavaScript runtime executing on the current thread
/// * `value` must be a valid JavaScript object associated with the same runtime as `env`
pub unsafe fn from_value<T: ?Sized>(env: napi::Env, value: napi::Value) -> Result<T, Error>
where
T: Deserialize<'static>,
{
T::deserialize(de::Deserializer::new(env, value))
}
/// Attempts to write Rust data into a JavaScript value using the serde::Serialize implementation
/// # Safety
/// * The returned `napi::Value` must not outlive the `env` parameter
/// * `env` must point to the JavaScript runtime executing on the current thread
pub unsafe fn to_value<T: ?Sized>(env: napi::Env, value: &T) -> Result<napi::Value, Error>
where
T: Serialize,
{
value.serialize(se::Serializer::new(env))
}

View File

@ -0,0 +1,432 @@
//! Implements a serde serializer for JavaScript values
//!
//! # Safety
//!
//! All JavaScript types are neither `Send` or `Sync`. Threads should be used.
use conv::ValueFrom;
use serde_crate::{ser, Serialize};
use super::*;
use crate::napi;
#[derive(Clone, Copy)]
#[repr(transparent)]
/// High level deserializer for all JavaScript values
pub(super) struct Serializer {
env: napi::Env,
}
impl Serializer {
pub(super) fn new(env: napi::Env) -> Self {
Self { env }
}
}
// Specialized serializer for writing to an `Array`
pub(super) struct ArraySerializer {
serializer: Serializer,
value: napi::Value,
offset: usize,
}
impl ArraySerializer {
fn new(serializer: Serializer, value: napi::Value) -> Self {
Self {
serializer,
value,
offset: 0,
}
}
}
// `Array` serializer for externally tagged enum `{ [key]: value }`
pub(super) struct WrappedArraySerializer {
serializer: ArraySerializer,
value: napi::Value,
}
impl WrappedArraySerializer {
fn new(serializer: ArraySerializer, value: napi::Value) -> Self {
Self { serializer, value }
}
}
// Specialized serializer for writing to a generic `Object`
pub(super) struct ObjectSerializer {
serializer: Serializer,
value: napi::Value,
key: Option<napi::Value>,
}
impl ObjectSerializer {
fn new(serializer: Serializer, value: napi::Value) -> Self {
Self {
serializer,
value,
key: None,
}
}
}
// `Object` serializer for externally tagged enum `{ [key]: value }`
pub(super) struct WrappedObjectSerializer {
serializer: ObjectSerializer,
value: napi::Value,
}
impl WrappedObjectSerializer {
fn new(serializer: ObjectSerializer, value: napi::Value) -> Self {
Self { serializer, value }
}
}
impl ser::Serializer for Serializer {
type Ok = napi::Value;
type Error = Error;
// Limited JavaScript types require sequences and tuples to both use `Array`
type SerializeSeq = ArraySerializer;
type SerializeTuple = ArraySerializer;
type SerializeTupleStruct = ArraySerializer;
type SerializeTupleVariant = WrappedArraySerializer;
type SerializeMap = ObjectSerializer;
type SerializeStruct = ObjectSerializer;
type SerializeStructVariant = WrappedObjectSerializer;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
Ok(create_bool(self.env, v)?)
}
// All numeric types are serialized into `f64`
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
let v = f64::value_from(v)?;
Ok(create_double(self.env, v)?)
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
let v = f64::value_from(v)?;
Ok(create_double(self.env, v)?)
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
Ok(create_double(self.env, v)?)
}
// `char` are serialized as single character string
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
Ok(create_string(self.env, v.to_string())?)
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
Ok(create_string(self.env, v)?)
}
// Bytes are serialized as `ArrayBuffer`
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok, Self::Error> {
Ok(create_arraybuffer(self.env, v)?)
}
// `None` is serialized as a `null`
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
self.serialize_unit()
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
// JavaScript does not have a unit type; `null` is used instead
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Ok(get_null(self.env)?)
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
self.serialize_unit()
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
self.serialize_str(variant)
}
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: ?Sized + Serialize,
{
let o = create_object(self.env)?;
let k = create_string(self.env, variant)?;
let v = value.serialize(self)?;
object_set(self.env, o, k, v)?;
Ok(o)
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
let len = len.unwrap_or_default();
let value = create_array_with_length(self.env, len)?;
Ok(ArraySerializer::new(self, value))
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
self.serialize_seq(Some(len))
}
// Externally tagged enum; `{ [key]: value }`
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
let env = self.env;
let wrapper = create_object(env)?;
let arr = create_array_with_length(env, len)?;
let k = create_string(env, variant)?;
let serializer = ArraySerializer::new(self, arr);
object_set(env, wrapper, k, arr)?;
Ok(WrappedArraySerializer::new(serializer, wrapper))
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
let value = create_object(self.env)?;
Ok(ObjectSerializer::new(self, value))
}
fn serialize_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
self.serialize_map(Some(len))
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
let env = self.env;
let wrapper = create_object(env)?;
let value = create_object(env)?;
let k = create_string(env, variant)?;
let serializer = ObjectSerializer::new(self, value);
object_set(env, wrapper, k, value)?;
Ok(WrappedObjectSerializer::new(serializer, wrapper))
}
}
impl<'a> ser::SerializeSeq for ArraySerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
let value = value.serialize(self.serializer)?;
let k = u32::value_from(self.offset)?;
array_set(self.serializer.env, self.value, k, value)?;
self.offset += 1;
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}
impl<'a> ser::SerializeTuple for ArraySerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
ser::SerializeSeq::end(self)
}
}
impl<'a> ser::SerializeTupleStruct for ArraySerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
ser::SerializeSeq::end(self)
}
}
impl<'a> ser::SerializeTupleVariant for WrappedArraySerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeSeq::serialize_element(&mut self.serializer, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}
impl ser::SerializeMap for ObjectSerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_key<T>(&mut self, key: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
self.key = Some(key.serialize(self.serializer)?);
Ok(())
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
let k = self.key.ok_or_else(Error::missing_key)?;
let v = value.serialize(self.serializer)?;
object_set(self.serializer.env, self.value, k, v)?;
Ok(())
}
fn serialize_entry<K, V>(&mut self, key: &K, value: &V) -> Result<(), Self::Error>
where
K: ?Sized + Serialize,
V: ?Sized + Serialize,
{
let k = key.serialize(self.serializer)?;
let v = value.serialize(self.serializer)?;
object_set(self.serializer.env, self.value, k, v)?;
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}
impl ser::SerializeStruct for ObjectSerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeMap::serialize_entry(self, key, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}
impl ser::SerializeStructVariant for WrappedObjectSerializer {
type Ok = napi::Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: ?Sized + Serialize,
{
ser::SerializeMap::serialize_entry(&mut self.serializer, key, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(self.value)
}
}

View File

@ -168,7 +168,8 @@ impl<T: Send + 'static> ThreadsafeFunction<T> {
impl<T> Drop for ThreadsafeFunction<T> {
fn drop(&mut self) {
unsafe {
napi::release_threadsafe_function(
// Status is ignored because cannot take any action if there was an error
let _ = napi::release_threadsafe_function(
self.tsfn.0,
napi::ThreadsafeFunctionReleaseMode::Release,
);

View File

@ -1,5 +1,8 @@
//! Node _execution contexts_, which manage access to the JavaScript engine at various points in the Node.js runtime lifecycle.
#[cfg(all(feature = "napi-1", feature = "serde"))]
use serde_crate::{Deserialize, Serialize};
pub(crate) mod internal;
use borrow::internal::Ledger;
@ -13,6 +16,8 @@ use neon_runtime::raw;
#[cfg(feature = "legacy-runtime")]
use object::class::Class;
use object::{Object, This};
#[cfg(all(feature = "napi-1", feature = "serde"))]
use result::SerdeError;
use result::{JsResult, NeonResult, Throw};
#[cfg(feature = "napi-1")]
use smallvec::SmallVec;
@ -404,6 +409,27 @@ pub trait Context<'a>: ContextInternal<'a> {
fn queue(&mut self) -> EventQueue {
EventQueue::new(self)
}
#[cfg(all(feature = "napi-1", feature = "serde"))]
#[allow(clippy::wrong_self_convention)]
/// Attempt to write a struct into a JavaScript value using the serde::ser::Serialize implementation
fn to_js_value<T: ?Sized>(&mut self, v: &T) -> Result<Handle<'a, JsValue>, SerdeError>
where
T: Serialize,
{
let result = unsafe { neon_runtime::serde::to_value(self.env().to_raw(), v) };
result.map(JsValue::new_internal)
}
#[cfg(all(feature = "napi-1", feature = "serde"))]
/// Attempt to read a struct into a JavaScript value using the serde::de::Deserialize implementation
fn from_js_value<T: ?Sized, U: Value>(&mut self, v: Handle<U>) -> Result<T, SerdeError>
where
T: Deserialize<'static>,
{
unsafe { neon_runtime::serde::from_value(self.env().to_raw(), v.to_raw()) }
}
}
/// A view of the JS engine in the context of top-level initialization of a Neon module.

View File

@ -3,6 +3,8 @@
extern crate cslice;
extern crate neon_runtime;
extern crate semver;
#[cfg(all(feature = "napi-1", feature = "serde"))]
extern crate serde_crate;
extern crate smallvec;
#[cfg(feature = "proc-macros")]

View File

@ -21,7 +21,7 @@ pub use handle::Handle;
#[cfg(feature = "legacy-runtime")]
pub use object::Class;
pub use object::Object;
pub use result::{JsResult, JsResultExt, NeonResult};
pub use result::{JsResult, JsResultExt, NeonResult, ResultExt};
#[cfg(feature = "legacy-runtime")]
pub use task::Task;
pub use types::{

View File

@ -1,5 +1,8 @@
//! Types and traits for working with JavaScript exceptions.
#[cfg(all(feature = "napi-1", feature = "serde"))]
pub use neon_runtime::serde::Error as SerdeError;
use context::Context;
use handle::Handle;
use std::fmt::{Display, Formatter, Result as FmtResult};
@ -31,3 +34,25 @@ pub type JsResult<'b, T> = NeonResult<Handle<'b, T>>;
pub trait JsResultExt<'a, V: Value> {
fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, V>;
}
/// An extension trait for `Result` values that can be converted into `NeonResult` values by throwing a JavaScript
/// exception in the error case.
pub trait ResultExt<T> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T>;
}
#[cfg(all(feature = "napi-1", feature = "serde"))]
impl<T> ResultExt<T> for Result<T, SerdeError> {
fn or_throw<'a, C: Context<'a>>(self, cx: &mut C) -> NeonResult<T> {
let err = match self {
Ok(v) => return Ok(v),
Err(err) => err,
};
if err.is_exception_pending() {
Err(Throw)
} else {
cx.throw_error(err.to_string())
}
}
}

View File

@ -9,8 +9,12 @@ edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
[dependencies.neon]
version = "*"
path = "../.."
default-features = false
features = ["default-panic-hook", "napi-6", "try-catch-api", "event-queue-api"]
features = ["default-panic-hook", "napi-6", "try-catch-api", "event-queue-api", "serde"]

File diff suppressed because it is too large Load Diff

21
test/napi/lib/serde.js Normal file
View File

@ -0,0 +1,21 @@
"use strict";
const assert = require('assert');
const addon = require('..');
const pokedex = require('../fixtures/pokedex.json');
describe('serde', () => {
it('should be able to parse a pokedex from a string', () => {
const s = JSON.stringify(pokedex);
const result = addon.parse_pokedex(s);
assert.deepStrictEqual(result, pokedex);
});
it('should be able to stringify a pokedex', () => {
const result = addon.stringify_pokedex(pokedex);
assert.deepStrictEqual(JSON.parse(result), pokedex);
});
});

100
test/napi/src/js/serde.rs Normal file
View File

@ -0,0 +1,100 @@
// Pokedex example from https://app.quicktype.io/
use neon::prelude::*;
use serde::{Deserialize, Serialize};
/// A collection of pokémon
#[derive(Serialize, Deserialize)]
pub struct PokedexSchema {
/// All pokémon contained in the pokédex
pokemon: Vec<Pokemon>,
}
/// A 'pocket monster.' One must catch them all.
#[derive(Serialize, Deserialize)]
pub struct Pokemon {
avg_spawns: f64,
/// The flavor of candy preferred by this pokémon
candy: String,
candy_count: Option<i64>,
egg: Egg,
height: String,
/// A unique identifier for this pokémon.
/// Higher ids generally imply rarer and more evolved pokémon.
id: i64,
/// Photographic evidence of this pokémon's existence
img: String,
multipliers: Option<Vec<f64>>,
name: String,
next_evolution: Option<Vec<Evolution>>,
num: String,
prev_evolution: Option<Vec<Evolution>>,
spawn_chance: f64,
spawn_time: String,
#[serde(rename = "type")]
pokemon_type: Vec<Type>,
/// Types of pokémon that cause extra damage to this pokémon
weaknesses: Vec<Type>,
weight: String,
}
/// A description of an evolutionary stage of a pokémon
#[derive(Serialize, Deserialize)]
pub struct Evolution {
/// The name of the Pokémon to or from which the containing Pokémon evolves
name: String,
/// The number of the pokémon to or from which the containing pokémon evolves
num: String,
}
#[derive(Serialize, Deserialize)]
pub enum Egg {
#[serde(rename = "Not in Eggs")]
NotInEggs,
#[serde(rename = "Omanyte Candy")]
OmanyteCandy,
#[serde(rename = "10 km")]
The10Km,
#[serde(rename = "2 km")]
The2Km,
#[serde(rename = "5 km")]
The5Km,
}
#[derive(Serialize, Deserialize)]
pub enum Type {
Bug,
Dark,
Dragon,
Electric,
Fairy,
Fighting,
Fire,
Flying,
Ghost,
Grass,
Ground,
Ice,
Normal,
Poison,
Psychic,
Rock,
Steel,
Water,
}
pub fn parse_pokedex(mut cx: FunctionContext) -> JsResult<JsValue> {
let pokedex = cx.argument::<JsString>(0)?.value(&mut cx);
let pokedex: PokedexSchema =
serde_json::from_str(&pokedex).or_else(|err| cx.throw_error(err.to_string()))?;
cx.to_js_value(&pokedex).or_throw(&mut cx)
}
pub fn stringify_pokedex(mut cx: FunctionContext) -> JsResult<JsString> {
let pokedex = cx.argument::<JsObject>(0)?;
let pokedex: PokedexSchema = cx.from_js_value(pokedex).or_throw(&mut cx)?;
let s = serde_json::to_string(&pokedex).or_else(|err| cx.throw_error(err.to_string()))?;
Ok(cx.string(s))
}

View File

@ -9,6 +9,7 @@ mod js {
pub mod functions;
pub mod numbers;
pub mod objects;
pub mod serde;
pub mod strings;
pub mod threads;
pub mod types;
@ -22,6 +23,7 @@ use js::errors::*;
use js::functions::*;
use js::numbers::*;
use js::objects::*;
use js::serde::*;
use js::strings::*;
use js::threads::*;
use js::types::*;
@ -257,5 +259,8 @@ fn main(mut cx: ModuleContext) -> NeonResult<()> {
cx.export_function("greeter_greet", greeter_greet)?;
cx.export_function("leak_event_queue", leak_event_queue)?;
cx.export_function("parse_pokedex", parse_pokedex)?;
cx.export_function("stringify_pokedex", stringify_pokedex)?;
Ok(())
}