use super::*;
use serde::de::*;
impl<'de, T: Deserialize<'de>> ReadJs for T {
fn read_js(ctx: &DukContext, obj_index: i32) -> Result<Self, JsError> {
Self::deserialize(JsEngineDeserializer::new(ctx, obj_index))
}
}
pub struct JsEngineDeserializer<'a> {
ctx: &'a DukContext,
index: i32,
len: usize,
}
impl <'a> JsEngineDeserializer<'a> {
pub fn new(ctx: &'a DukContext, index: i32) -> Self {
Self { ctx, index, len: 0 }
}
}
impl<'de, 'a> Deserializer<'de> for JsEngineDeserializer<'a> {
type Error = JsError;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
use super::DukType::*;
match self.ctx.get_type(self.index) {
DUK_TYPE_UNDEFINED | DUK_TYPE_NULL => visitor.visit_none(),
DUK_TYPE_BOOLEAN => visitor.visit_bool(self.ctx.get_boolean(self.index)),
DUK_TYPE_NUMBER => {
let n = self.ctx.get_number(self.index);
if n.is_finite() && (n.trunc() - n).abs() < f64::EPSILON {
visitor.visit_i64(n as i64)
} else {
visitor.visit_f64(n)
}
}
DUK_TYPE_STRING => visitor.visit_str(&self.ctx.get_string(self.index)),
DUK_TYPE_BUFFER => visitor.visit_bytes(self.ctx.get_buffer(self.index)),
DUK_TYPE_OBJECT => {
if self.ctx.is_array(self.index) {
let len = self.ctx.get_length( self.index);
self.ctx.enum_indices(self.index);
let res = visitor.visit_seq(JsEngineDeserializer { ctx: self.ctx, index: -1, len });
self.ctx.pop();
res
} else if self.ctx.is_pure_object(self.index) {
self.ctx.enum_keys(self.index);
let res = visitor.visit_map(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 });
self.ctx.pop();
res
} else {
return Err(JsError::from(format!("Unimplemented javascript object type"))); }
}
_ => return Err(JsError::from(format!("Unimplemented javascript object type"))) }
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
use super::DukType::{DUK_TYPE_NULL, DUK_TYPE_UNDEFINED};
match self.ctx.get_type(self.index) {
DUK_TYPE_UNDEFINED | DUK_TYPE_NULL => visitor.visit_none(),
_ => visitor.visit_some(self)
}
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
visitor.visit_unit()
}
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_tuple_struct<V>(self, _name: &'static str, _len: usize, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_struct<V>(self, _name: &'static str, _fields: &'static [&'static str], visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_enum<V>(self, _name: &'static str, _variants: &'static [&'static str], visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
use super::DukType::*;
match self.ctx.get_type(self.index) {
DUK_TYPE_STRING => {
visitor.visit_enum(self.ctx.get_string(self.index).into_deserializer())
}
DUK_TYPE_OBJECT if self.ctx.is_pure_object(self.index) => {
self.ctx.enum_keys(self.index);
if !self.ctx.next(-1) {
self.ctx.pop();
return Err(JsError::from(String::from(
"expected an enum object with a single variant key, got an empty object",
)));
}
if self.ctx.next(-3) {
self.ctx.pop_n(2); self.ctx.pop_n(2); self.ctx.pop(); return Err(JsError::from(String::from(
"expected an enum object with a single variant key, got multiple keys",
)));
}
let res = visitor.visit_enum(JsEnumAccess { ctx: self.ctx });
self.ctx.pop_n(2); self.ctx.pop(); res
}
_ => Err(JsError::from(String::from(
"expected a string or an object to deserialize an enum",
))),
}
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error> where V: Visitor<'de> {
self.deserialize_any(visitor)
}
}
struct JsEnumAccess<'a> {
ctx: &'a DukContext,
}
impl<'de, 'a> EnumAccess<'de> for JsEnumAccess<'a> {
type Error = JsError;
type Variant = Self;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where V: DeserializeSeed<'de>
{
let variant = seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -2, len: 0 })?;
Ok((variant, self))
}
}
impl<'de, 'a> VariantAccess<'de> for JsEnumAccess<'a> {
type Error = JsError;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
where T: DeserializeSeed<'de>
{
seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 })
}
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value, Self::Error>
where V: Visitor<'de>
{
JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 }.deserialize_seq(visitor)
}
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V)
-> Result<V::Value, Self::Error> where V: Visitor<'de>
{
JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 }.deserialize_map(visitor)
}
}
impl<'de, 'a> MapAccess<'de> for JsEngineDeserializer<'a> {
type Error = JsError;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error> where K: DeserializeSeed<'de> {
if self.ctx.next(-1) {
Ok(Some(seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -2, len: 0 })?))
} else {
Ok(None)
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error> where V: DeserializeSeed<'de> {
let res = seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 });
self.ctx.pop_n(2);
res
}
fn next_entry_seed<K, V>(&mut self, kseed: K, vseed: V) -> Result<Option<(K::Value, V::Value)>, Self::Error> where K: DeserializeSeed<'de>, V: DeserializeSeed<'de> {
if self.ctx.next(-1) {
let k = kseed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -2, len: 0 })?;
let v = vseed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 })?;
self.ctx.pop_n(2);
Ok(Some((k, v)))
} else {
Ok(None)
}
}
}
impl<'de, 'a> SeqAccess<'de> for JsEngineDeserializer<'a> {
type Error = JsError;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error> where T: DeserializeSeed<'de> {
if self.ctx.next(-1) {
let v = seed.deserialize(JsEngineDeserializer { ctx: self.ctx, index: -1, len: 0 })?;
self.ctx.pop_n(2);
Ok(Some(v))
} else {
Ok(None)
}
}
fn size_hint(&self) -> Option<usize> {
Some(self.len)
}
}
#[cfg(test)]
mod tests {
use super::*;
use smart_default::SmartDefault;
use serde::{Serialize, Deserialize};
fn deserialize<'a, T: std::fmt::Debug + Serialize + Deserialize<'a> + Default>(value: &T) {
let e = JsEngine::new().unwrap();
e.write(value).unwrap_or_else(|err| {
panic!("{}", err);
});
e.put_global_string("value");
e.get_global_string("value");
let val: T = e.read_top().unwrap_or_else(|err| {
panic!("{}", err);
});
assert_eq!(format!("{:?}", value), format!("{:?}", val));
}
fn deserialize_expr<'a, T: std::fmt::Debug + Deserialize<'a>>(expr: &str) -> T {
let e = JsEngine::new().unwrap();
e.eval(expr).unwrap();
e.get_global_string("value");
let val: T = e.read_top().unwrap_or_else(|err| {
panic!("{}", err);
});
val
}
fn test_deserialize<'a, T: std::fmt::Debug + Serialize + Deserialize<'a> + Default>(value: &T) {
deserialize(value);
}
#[derive(Debug, SmartDefault, Serialize, Deserialize)]
struct TestStruct {
#[default = "string value"]
string_field: String,
#[default = 'A']
char_field: char,
#[default = 1]
i8_field: i8,
#[default(_code = "vec![1.0,2.0,3.0,7.5]")]
arr_field: Vec<f64>,
optional1: Option<f64>,
optional2: Option<f64>,
unit: ()
}
#[test]
fn read_struct() {
let mut p = TestStruct::default();
p.char_field = 'B';
p.i8_field = 44;
p.optional1 = Some(3.14);
test_deserialize(&p);
}
#[test]
fn deserialize_zero() {
#[derive(Debug, Deserialize)]
struct TestStruct {
float_field: f64,
int_field: i64,
}
let val: TestStruct = deserialize_expr(r#"
value = {
float_field: 0,
int_field: 0
};
"#);
assert_eq!(val.float_field, 0.0);
assert_eq!(val.int_field, 0);
}
#[test]
fn deserialize_nan() {
#[derive(Debug, Deserialize)]
struct TestStruct {
float_field: f64,
}
let val: TestStruct = deserialize_expr(r#"
value = {
float_field: NaN
};
"#);
assert!(val.float_field.is_nan());
}
#[test]
fn deserialize_unit() {
let _val: () = deserialize_expr(r#"
value = {
test: "asfads"
};
"#);
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum HttpMethod { Get, Post, Delete }
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum Shape {
Empty,
Radius(f64),
Pair(i64, i64),
Rect { w: i64, h: i64 },
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Req { url: String, method: HttpMethod }
#[test]
fn bare_unit_variant_from_string() {
let val: HttpMethod = deserialize_expr("value = 'post'");
assert_eq!(val, HttpMethod::Post);
let val: HttpMethod = deserialize_expr("value = 'get'");
assert_eq!(val, HttpMethod::Get);
}
#[test]
fn unit_variant_as_struct_field() {
let val: Req = deserialize_expr(r#"
value = {
url: 'http://x',
method: 'delete'
};
"#);
assert_eq!(val, Req { url: "http://x".to_string(), method: HttpMethod::Delete });
}
#[test]
fn unknown_variant_is_a_clean_error() {
let e = JsEngine::new().unwrap();
e.eval("value = 'teapot'").unwrap();
e.get_global_string("value");
let res: Result<HttpMethod, _> = e.read_top();
let err = res.unwrap_err();
assert!(err.to_string().contains("unknown variant"), "error was: {}", err);
}
#[test]
fn tagged_variants_from_js_objects() {
let val: Shape = deserialize_expr("value = ({Radius: 2.5})");
assert_eq!(val, Shape::Radius(2.5));
let val: Shape = deserialize_expr("value = ({Pair: [1,2]})");
assert_eq!(val, Shape::Pair(1, 2));
let val: Shape = deserialize_expr("value = ({Rect: {w:3,h:4}})");
assert_eq!(val, Shape::Rect { w: 3, h: 4 });
}
#[test]
fn empty_object_variant_is_a_clean_error() {
let e = JsEngine::new().unwrap();
e.eval("value = ({})").unwrap();
e.get_global_string("value");
let res: Result<Shape, _> = e.read_top();
let err = res.unwrap_err();
assert!(err.to_string().contains("empty object"), "error was: {}", err);
}
#[test]
fn multi_key_object_variant_is_a_clean_error() {
let e = JsEngine::new().unwrap();
e.eval("value = ({Radius: 2.5, Extra: 999})").unwrap();
e.get_global_string("value");
let res: Result<Shape, _> = e.read_top();
let err = res.unwrap_err();
assert!(err.to_string().contains("multiple keys"), "error was: {}", err);
}
#[test]
fn all_variant_shapes_round_trip() {
for shape in [
Shape::Empty,
Shape::Radius(2.5),
Shape::Pair(1, 2),
Shape::Rect { w: 3, h: 4 },
] {
let e = JsEngine::new().unwrap();
e.write(&shape).unwrap();
e.put_global_string("value");
e.get_global_string("value");
let val: Shape = e.read_top().unwrap();
assert_eq!(shape, val);
}
let req = Req { url: "http://x".to_string(), method: HttpMethod::Post };
let e = JsEngine::new().unwrap();
e.write(&req).unwrap();
e.put_global_string("value");
e.get_global_string("value");
let val: Req = e.read_top().unwrap();
assert_eq!(req, val);
}
}