Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 110 additions & 34 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use rquickjs::{
},
};
use serde::{
de::{self, IntoDeserializer, Unexpected},
de::{self, IntoDeserializer},
forward_to_deserialize_any,
};

Expand Down Expand Up @@ -292,16 +292,30 @@ impl<'de> de::Deserializer<'de> for &mut Deserializer<'de> {
self.value = v;
}

// Now require a primitive string.
let s = if let Some(s) = self.value.as_string() {
s.to_string()
.unwrap_or_else(|e| to_string_lossy(self.value.ctx(), s, e))
} else {
return Err(Error::new("expected a string for enum unit variant"));
};
if let Some(obj) = self.value.as_object() {
let (variant, value): (String, Value<'de>) = obj
.props::<String, Value>()
.next()
.ok_or_else(|| Error::new("expected enum object with one key"))?
.map_err(Error::new)?;

// Hand Serde an EnumAccess that only supports unit variants.
visitor.visit_enum(UnitEnumAccess { variant: s })
visitor.visit_enum(EnumAccessImpl {
variant,
value: Some(value.clone()),
})
} else if let Some(s) = self.value.as_string() {
// Now require a primitive string.
let s = s
.to_string()
.unwrap_or_else(|e| to_string_lossy(self.value.ctx(), s, e));

visitor.visit_enum(EnumAccessImpl {
variant: s,
value: None,
})
} else {
Err(Error::new("expected a string or object for enum"))
}
}

forward_to_deserialize_any! {
Expand Down Expand Up @@ -592,60 +606,69 @@ fn get_index<'a>(obj: &Object<'a>, idx: usize) -> rquickjs::Result<Value<'a>> {
}

/// A helper struct for deserializing enums containing unit variants.
struct UnitEnumAccess {
struct EnumAccessImpl<'de> {
variant: String,
value: Option<Value<'de>>,
}

impl<'de> de::EnumAccess<'de> for UnitEnumAccess {
impl<'de> de::EnumAccess<'de> for EnumAccessImpl<'de> {
type Error = Error;
type Variant = UnitOnlyVariant;
type Variant = VariantAccessImpl<'de>;

fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
where
V: de::DeserializeSeed<'de>,
{
let v = seed.deserialize(self.variant.into_deserializer())?;
Ok((v, UnitOnlyVariant))
let val = seed.deserialize(self.variant.into_deserializer())?;
Ok((val, VariantAccessImpl { value: self.value }))
}
}

struct UnitOnlyVariant;
struct VariantAccessImpl<'de> {
value: Option<Value<'de>>,
}

impl<'de> de::VariantAccess<'de> for UnitOnlyVariant {
impl<'de> de::VariantAccess<'de> for VariantAccessImpl<'de> {
type Error = Error;

fn unit_variant(self) -> Result<()> {
Ok(())
match self.value {
None => Ok(()),
Some(_) => Err(Error::new("expected unit variant")),
}
}

fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value>
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>
where
T: de::DeserializeSeed<'de>,
{
Err(de::Error::invalid_type(
Unexpected::NewtypeVariant,
&"unit variant",
))
let value = self
.value
.ok_or_else(|| Error::new("expected value for newtype variant"))?;

seed.deserialize(&mut Deserializer::from(value))
}

fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value>
fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
Err(de::Error::invalid_type(
Unexpected::TupleVariant,
&"unit variant",
))
let value = self
.value
.ok_or_else(|| Error::new("expected tuple variant"))?;

de::Deserializer::deserialize_seq(&mut Deserializer::from(value), visitor)
}

fn struct_variant<V>(self, _fields: &'static [&'static str], _visitor: V) -> Result<V::Value>
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
Err(de::Error::invalid_type(
Unexpected::StructVariant,
&"unit variant",
))
let value = self
.value
.ok_or_else(|| Error::new("expected struct variant"))?;

de::Deserializer::deserialize_map(&mut Deserializer::from(value), visitor)
}
}

Expand Down Expand Up @@ -887,7 +910,7 @@ mod tests {
}

#[test]
fn test_enum() {
fn test_enum_unit() {
let rt = Runtime::default();

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
Expand All @@ -905,6 +928,59 @@ mod tests {
});
}

#[test]
fn test_enum_newtype() {
let rt = Runtime::default();

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
enum Test {
One(i32),
Two(i32),
}

rt.context().with(|cx| {
let left = Test::One(6);
let value = to_value(cx, left).unwrap();
let right: Test = from_value(value).unwrap();
assert_eq!(left, right);
});
}

#[test]
fn test_enum_struct() {
let rt = Runtime::default();

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
enum Test {
One { a: i32 },
Two(i32),
}

rt.context().with(|cx| {
let left = Test::One { a: 6 };
let value = to_value(cx, left).unwrap();
let right: Test = from_value(value).unwrap();
assert_eq!(left, right);
});
}

#[test]
fn test_enum_tuple() {
let rt = Runtime::default();

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
enum Test {
One(i32, i32),
}

rt.context().with(|cx| {
let left = Test::One(1, 2);
let value = to_value(cx, left).unwrap();
let right: Test = from_value(value).unwrap();
assert_eq!(left, right);
});
}

#[test]
fn test_short_bigint() {
let rt = Runtime::default();
Expand Down
7 changes: 3 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ where
T: Serialize,
{
let mut serializer = Serializer::from_context(context)?;
value.serialize(&mut serializer)?;
Ok(serializer.value)
value.serialize(&mut serializer)
}

/// Interpret a `rquickjs::Value` as an instance of type `T`.
Expand Down Expand Up @@ -418,8 +417,8 @@ mod tests {
let rt = Runtime::default();
rt.context().with(|cx| {
let mut serializer = ValueSerializer::from_context(cx).unwrap();
expected.serialize(&mut serializer).unwrap();
let mut deserializer = ValueDeserializer::from(serializer.value);
let value = expected.serialize(&mut serializer).unwrap();
let mut deserializer = ValueDeserializer::from(value);
A::deserialize(&mut deserializer).unwrap()
})
}
Expand Down
Loading
Loading