use std::fmt::{self, Display};
use rhai::{Array, Blob, Dynamic, ImmutableString, Map};
use serde::de::{
self, DeserializeOwned, DeserializeSeed, EnumAccess, IntoDeserializer, MapAccess, SeqAccess,
VariantAccess, Visitor,
};
use serde::forward_to_deserialize_any;
#[derive(Debug)]
pub struct Error(String);
impl Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
impl std::error::Error for Error {}
impl de::Error for Error {
fn custom<T: Display>(message: T) -> Self {
Error(message.to_string())
}
}
pub fn from_dynamic<T: DeserializeOwned>(value: &Dynamic) -> Result<T, Error> {
T::deserialize(DynamicDeserializer(value))
}
struct DynamicDeserializer<'a>(&'a Dynamic);
macro_rules! deserialize_as_int {
($method:ident, $visit:ident, $target:ty) => {
fn $method<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
let value = self
.0
.as_int()
.map_err(|_| Error(format!("expected an integer, found {}", self.0.type_name())))?;
visitor.$visit(value as $target)
}
};
}
impl<'de> de::Deserializer<'de> for DynamicDeserializer<'_> {
type Error = Error;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
let value = self.0;
if value.is_unit() {
visitor.visit_unit()
} else if value.is_bool() {
visitor.visit_bool(value.as_bool().unwrap())
} else if value.is_int() {
visitor.visit_i64(value.as_int().unwrap())
} else if value.is_float() {
visitor.visit_f64(value.as_float().unwrap())
} else if value.is_char() {
visitor.visit_char(value.as_char().unwrap())
} else if value.is_string() {
self.deserialize_str(visitor)
} else if value.is_array() || value.is_blob() {
self.deserialize_seq(visitor)
} else if value.is_map() {
self.deserialize_map(visitor)
} else {
Err(Error(format!("unsupported value: {}", value.type_name())))
}
}
fn deserialize_bool<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
let value = self
.0
.as_bool()
.map_err(|_| Error(format!("expected a bool, found {}", self.0.type_name())))?;
visitor.visit_bool(value)
}
deserialize_as_int!(deserialize_i8, visit_i8, i8);
deserialize_as_int!(deserialize_i16, visit_i16, i16);
deserialize_as_int!(deserialize_i32, visit_i32, i32);
deserialize_as_int!(deserialize_i64, visit_i64, i64);
deserialize_as_int!(deserialize_u8, visit_u8, u8);
deserialize_as_int!(deserialize_u16, visit_u16, u16);
deserialize_as_int!(deserialize_u32, visit_u32, u32);
deserialize_as_int!(deserialize_u64, visit_u64, u64);
fn deserialize_f32<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
visitor.visit_f32(self.float()? as f32)
}
fn deserialize_f64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
visitor.visit_f64(self.float()?)
}
fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
let value = self
.0
.as_char()
.map_err(|_| Error(format!("expected a char, found {}", self.0.type_name())))?;
visitor.visit_char(value)
}
fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
let guard = self
.0
.read_lock::<ImmutableString>()
.ok_or_else(|| Error(format!("expected a string, found {}", self.0.type_name())))?;
visitor.visit_str(guard.as_str())
}
fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
self.deserialize_str(visitor)
}
fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
if let Some(blob) = self.0.read_lock::<Blob>() {
return visitor.visit_bytes(&blob);
}
self.deserialize_seq(visitor)
}
fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
self.deserialize_bytes(visitor)
}
fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
if self.0.is_unit() {
visitor.visit_none()
} else {
visitor.visit_some(self)
}
}
fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
visitor.visit_unit()
}
fn deserialize_unit_struct<V: Visitor<'de>>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Error> {
visitor.visit_unit()
}
fn deserialize_newtype_struct<V: Visitor<'de>>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Error> {
visitor.visit_newtype_struct(self)
}
fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
if let Some(array) = self.0.read_lock::<Array>() {
return visitor.visit_seq(SeqDeserializer { iter: array.iter() });
}
if let Some(blob) = self.0.read_lock::<Blob>() {
return visitor.visit_seq(BlobDeserializer { iter: blob.iter() });
}
Err(Error(format!(
"expected an array, found {}",
self.0.type_name()
)))
}
fn deserialize_tuple<V: Visitor<'de>>(
self,
_len: usize,
visitor: V,
) -> Result<V::Value, Error> {
self.deserialize_seq(visitor)
}
fn deserialize_tuple_struct<V: Visitor<'de>>(
self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value, Error> {
self.deserialize_seq(visitor)
}
fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
let map = self
.0
.read_lock::<Map>()
.ok_or_else(|| Error(format!("expected a map, found {}", self.0.type_name())))?;
visitor.visit_map(MapDeserializer {
iter: map.iter().map(|(key, value)| (key.as_str(), value)),
value: None,
})
}
fn deserialize_struct<V: Visitor<'de>>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error> {
self.deserialize_map(visitor)
}
fn deserialize_enum<V: Visitor<'de>>(
self,
name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error> {
if name == "Ref" && self.0.is_int() {
let id = ((self.0.as_int().unwrap() as u64) >> 32) as u32;
return visitor.visit_enum(ExistingEnum(id));
}
if let Some(name) = self.0.read_lock::<ImmutableString>() {
return visitor.visit_enum(EnumDeserializer {
variant: name.as_str(),
value: None,
});
}
if let Some(map) = self.0.read_lock::<Map>() {
let mut entries = map.iter();
let (key, value) = entries
.next()
.ok_or_else(|| Error("expected a one-entry map for an enum".to_string()))?;
if entries.next().is_some() {
return Err(Error("expected a single enum variant".to_string()));
}
return visitor.visit_enum(EnumDeserializer {
variant: key.as_str(),
value: Some(value),
});
}
Err(Error(format!(
"expected a string or map for an enum, found {}",
self.0.type_name()
)))
}
fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
self.deserialize_str(visitor)
}
fn deserialize_ignored_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
self.deserialize_any(visitor)
}
}
impl DynamicDeserializer<'_> {
fn float(&self) -> Result<f64, Error> {
if self.0.is_float() {
Ok(self.0.as_float().unwrap())
} else if self.0.is_int() {
Ok(self.0.as_int().unwrap() as f64)
} else {
Err(Error(format!(
"expected a number, found {}",
self.0.type_name()
)))
}
}
}
struct SeqDeserializer<'a> {
iter: std::slice::Iter<'a, Dynamic>,
}
impl<'de> SeqAccess<'de> for SeqDeserializer<'_> {
type Error = Error;
fn next_element_seed<T: DeserializeSeed<'de>>(
&mut self,
seed: T,
) -> Result<Option<T::Value>, Error> {
match self.iter.next() {
Some(value) => seed.deserialize(DynamicDeserializer(value)).map(Some),
None => Ok(None),
}
}
}
struct BlobDeserializer<'a> {
iter: std::slice::Iter<'a, u8>,
}
impl<'de> SeqAccess<'de> for BlobDeserializer<'_> {
type Error = Error;
fn next_element_seed<T: DeserializeSeed<'de>>(
&mut self,
seed: T,
) -> Result<Option<T::Value>, Error> {
match self.iter.next() {
Some(byte) => seed.deserialize((*byte).into_deserializer()).map(Some),
None => Ok(None),
}
}
}
struct MapDeserializer<'a, I> {
iter: I,
value: Option<&'a Dynamic>,
}
impl<'de, 'a, I> MapAccess<'de> for MapDeserializer<'a, I>
where
I: Iterator<Item = (&'a str, &'a Dynamic)>,
{
type Error = Error;
fn next_key_seed<K: DeserializeSeed<'de>>(
&mut self,
seed: K,
) -> Result<Option<K::Value>, Error> {
match self.iter.next() {
Some((key, value)) => {
self.value = Some(value);
seed.deserialize(KeyDeserializer(key)).map(Some)
}
None => Ok(None),
}
}
fn next_value_seed<V: DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value, Error> {
let value = self
.value
.take()
.ok_or_else(|| Error("value requested before key".to_string()))?;
seed.deserialize(DynamicDeserializer(value))
}
}
struct KeyDeserializer<'a>(&'a str);
impl<'de> de::Deserializer<'de> for KeyDeserializer<'_> {
type Error = Error;
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Error> {
visitor.visit_str(self.0)
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes byte_buf
option unit unit_struct newtype_struct seq tuple tuple_struct map struct
enum identifier ignored_any
}
}
struct ExistingEnum(u32);
impl<'de> EnumAccess<'de> for ExistingEnum {
type Error = Error;
type Variant = ExistingVariant;
fn variant_seed<V: DeserializeSeed<'de>>(
self,
seed: V,
) -> Result<(V::Value, Self::Variant), Error> {
let variant = seed.deserialize(KeyDeserializer("Existing"))?;
Ok((variant, ExistingVariant(self.0)))
}
}
struct ExistingVariant(u32);
impl<'de> VariantAccess<'de> for ExistingVariant {
type Error = Error;
fn unit_variant(self) -> Result<(), Error> {
Err(Error("an entity handle needs an id".to_string()))
}
fn newtype_variant_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Value, Error> {
seed.deserialize(self.0.into_deserializer())
}
fn tuple_variant<V: Visitor<'de>>(self, _len: usize, _visitor: V) -> Result<V::Value, Error> {
Err(Error("an entity handle is not a tuple".to_string()))
}
fn struct_variant<V: Visitor<'de>>(
self,
_fields: &'static [&'static str],
_visitor: V,
) -> Result<V::Value, Error> {
Err(Error("an entity handle is not a struct".to_string()))
}
}
struct EnumDeserializer<'a> {
variant: &'a str,
value: Option<&'a Dynamic>,
}
impl<'de, 'a> EnumAccess<'de> for EnumDeserializer<'a> {
type Error = Error;
type Variant = VariantDeserializer<'a>;
fn variant_seed<V: DeserializeSeed<'de>>(
self,
seed: V,
) -> Result<(V::Value, Self::Variant), Error> {
let variant = seed.deserialize(KeyDeserializer(self.variant))?;
Ok((variant, VariantDeserializer { value: self.value }))
}
}
struct VariantDeserializer<'a> {
value: Option<&'a Dynamic>,
}
impl<'de> VariantAccess<'de> for VariantDeserializer<'_> {
type Error = Error;
fn unit_variant(self) -> Result<(), Error> {
match self.value {
None => Ok(()),
Some(_) => Err(Error("expected a unit variant".to_string())),
}
}
fn newtype_variant_seed<T: DeserializeSeed<'de>>(self, seed: T) -> Result<T::Value, Error> {
let value = self
.value
.ok_or_else(|| Error("expected a newtype variant payload".to_string()))?;
seed.deserialize(DynamicDeserializer(value))
}
fn tuple_variant<V: Visitor<'de>>(self, len: usize, visitor: V) -> Result<V::Value, Error> {
let value = self
.value
.ok_or_else(|| Error("expected a tuple variant payload".to_string()))?;
de::Deserializer::deserialize_tuple(DynamicDeserializer(value), len, visitor)
}
fn struct_variant<V: Visitor<'de>>(
self,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error> {
let value = self
.value
.ok_or_else(|| Error("expected a struct variant payload".to_string()))?;
de::Deserializer::deserialize_struct(DynamicDeserializer(value), "", fields, visitor)
}
}
#[cfg(test)]
mod tests {
use super::from_dynamic;
use crate::command::Command;
fn eval(source: &str) -> rhai::Dynamic {
rhai::Engine::new().eval::<rhai::Dynamic>(source).unwrap()
}
#[test]
fn draw_cube_coerces_integer_elements_to_f32() {
let value = eval(
"#{ DrawCube: #{ position: [0, 1, 0], scale: [1.0, 1.0, 1.0], color: [1.0, 0.0, 0.0, 1.0] } }",
);
let command = from_dynamic::<Command>(&value).unwrap();
assert_eq!(command.name(), "DrawCube");
}
#[test]
fn set_texture_reads_a_ref_and_a_string() {
let value = eval(r#"#{ SetTexture: #{ entity: #{ Result: 0 }, texture: "uv_test" } }"#);
let command = from_dynamic::<Command>(&value).unwrap();
assert_eq!(command.name(), "SetTexture");
}
#[test]
fn background_reads_a_unit_variant_from_a_string() {
let value = eval(r#"#{ SetBackground: #{ background: "Nebula" } }"#);
let command = from_dynamic::<Command>(&value).unwrap();
assert_eq!(command.name(), "SetBackground");
}
#[test]
fn entity_field_accepts_a_bare_handle_integer() {
let value = eval(r#"#{ SetTexture: #{ entity: 4294967296, texture: "uv_test" } }"#);
let command = from_dynamic::<Command>(&value).unwrap();
assert_eq!(command.name(), "SetTexture");
}
#[test]
fn unknown_command_is_an_error_not_a_panic() {
let value = eval("#{ NotACommand: #{} }");
assert!(from_dynamic::<Command>(&value).is_err());
}
}