use num_bigint::BigInt;
use num_traits::cast::FromPrimitive;
use serde::ser::{self, Serialize};
use std::convert::TryFrom;
use std::io;
use heck::SnakeCase;
use eetf::{self, Term};
use crate::error::{Error, Result};
pub fn to_writer<T, W>(value: &T, writer: &mut W) -> Result<()>
where
T: Serialize + ?Sized,
W: io::Write + ?Sized,
{
let serializer = Serializer {};
let term = value.serialize(&serializer)?;
match term.encode(writer) {
Ok(_result) => Ok(()),
Err(_error) => Err(Error::EncodeError("TODO".to_string())),
}
}
pub fn to_bytes<T>(value: &T) -> Result<Vec<u8>>
where
T: Serialize + ?Sized,
{
let mut cursor = io::Cursor::new(Vec::new());
match to_writer(value, &mut cursor) {
Ok(_) => Ok(cursor.into_inner()),
Err(e) => Err(e),
}
}
struct Serializer {}
struct SequenceSerializer {
items: Vec<Term>,
}
struct NamedSequenceSerializer {
name: Term,
items: Vec<Term>,
}
struct MapSerializer {
items: Vec<(Term, Term)>,
}
struct NamedMapSerializer {
name: Term,
items: Vec<(Term, Term)>,
}
impl<'a> ser::Serializer for &'a Serializer {
type Ok = Term;
type Error = Error;
type SerializeSeq = SequenceSerializer;
type SerializeTuple = SequenceSerializer;
type SerializeTupleStruct = SequenceSerializer;
type SerializeTupleVariant = NamedSequenceSerializer;
type SerializeMap = MapSerializer;
type SerializeStruct = MapSerializer;
type SerializeStructVariant = NamedMapSerializer;
fn serialize_bool(self, v: bool) -> Result<Term> {
Ok(Term::Atom(eetf::Atom::from(if v {
"true"
} else {
"false"
})))
}
fn serialize_i8(self, v: i8) -> Result<Term> {
self.serialize_i32(i32::from(v))
}
fn serialize_i16(self, v: i16) -> Result<Term> {
self.serialize_i32(i32::from(v))
}
fn serialize_i32(self, v: i32) -> Result<Term> {
Ok(Term::FixInteger(eetf::FixInteger { value: v }))
}
fn serialize_i64(self, v: i64) -> Result<Term> {
let big_int = BigInt::from_i64(v).expect("TODO: Handle failure here");
Ok(Term::BigInteger(eetf::BigInteger { value: big_int }))
}
fn serialize_u8(self, v: u8) -> Result<Term> {
self.serialize_u16(u16::from(v))
}
fn serialize_u16(self, v: u16) -> Result<Term> {
Ok(Term::FixInteger(eetf::FixInteger::from(v)))
}
fn serialize_u32(self, v: u32) -> Result<Term> {
self.serialize_u64(u64::from(v))
}
fn serialize_u64(self, v: u64) -> Result<Term> {
let big_int = BigInt::from_u64(v).expect("TODO: Handle failure here");
Ok(Term::BigInteger(eetf::BigInteger { value: big_int }))
}
fn serialize_f32(self, v: f32) -> Result<Term> {
self.serialize_f64(f64::from(v))
}
fn serialize_f64(self, v: f64) -> Result<Term> {
Ok(Term::Float(eetf::Float::try_from(v)?))
}
fn serialize_char(self, v: char) -> Result<Term> {
self.serialize_str(&v.to_string())
}
fn serialize_str(self, v: &str) -> Result<Term> {
Ok(Term::Binary(eetf::Binary::from(v.as_bytes())))
}
fn serialize_bytes(self, v: &[u8]) -> Result<Term> {
Ok(Term::Binary(eetf::Binary::from(v)))
}
fn serialize_none(self) -> Result<Term> {
Ok(Term::Atom(eetf::Atom::from("nil")))
}
fn serialize_some<T>(self, value: &T) -> Result<Term>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_unit(self) -> Result<Term> {
self.serialize_none()
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Term> {
self.serialize_unit()
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Term> {
Ok(Term::Atom(eetf::Atom::from(variant.to_snake_case())))
}
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Term>
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<Term>
where
T: ?Sized + Serialize,
{
let serialized_value = value.serialize(self)?;
Ok(Term::Tuple(eetf::Tuple::from(vec![
Term::Atom(eetf::Atom::from(variant.to_snake_case())),
serialized_value,
])))
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
let vec = match len {
None => Vec::new(),
Some(len) => Vec::with_capacity(len),
};
Ok(SequenceSerializer { items: vec })
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
Ok(SequenceSerializer {
items: Vec::with_capacity(len),
})
}
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct> {
self.serialize_tuple(len)
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant> {
Ok(NamedSequenceSerializer {
name: Term::Atom(eetf::Atom::from(variant.to_snake_case())),
items: Vec::with_capacity(len),
})
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
let vec = match len {
None => Vec::new(),
Some(len) => Vec::with_capacity(len),
};
Ok(MapSerializer { items: vec })
}
fn serialize_struct(self, _name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
self.serialize_map(Some(len))
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant> {
Ok(NamedMapSerializer {
name: Term::Atom(eetf::Atom::from(variant.to_snake_case())),
items: Vec::with_capacity(len),
})
}
}
impl<'a> ser::SerializeSeq for SequenceSerializer {
type Ok = Term;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let term_value = value.serialize(&Serializer {})?;
self.items.push(term_value);
Ok(())
}
fn end(self) -> Result<Term> {
Ok(Term::List(eetf::List {
elements: self.items,
}))
}
}
impl<'a> ser::SerializeTuple for SequenceSerializer {
type Ok = Term;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let term_value = value.serialize(&Serializer {})?;
self.items.push(term_value);
Ok(())
}
fn end(self) -> Result<Term> {
Ok(Term::Tuple(eetf::Tuple {
elements: self.items,
}))
}
}
impl<'a> ser::SerializeTupleStruct for SequenceSerializer {
type Ok = Term;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let term_value = value.serialize(&Serializer {})?;
self.items.push(term_value);
Ok(())
}
fn end(self) -> Result<Term> {
Ok(Term::Tuple(eetf::Tuple {
elements: self.items,
}))
}
}
impl<'a> ser::SerializeTupleVariant for NamedSequenceSerializer {
type Ok = Term;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let term_value = value.serialize(&Serializer {})?;
self.items.push(term_value);
Ok(())
}
fn end(self) -> Result<Term> {
let serialized_data = Term::Tuple(eetf::Tuple {
elements: self.items,
});
Ok(Term::Tuple(eetf::Tuple::from(vec![
self.name,
serialized_data,
])))
}
}
impl<'a> ser::SerializeMap for MapSerializer {
type Ok = Term;
type Error = Error;
fn serialize_key<T>(&mut self, _value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
panic!("Not Implemented")
}
fn serialize_value<T>(&mut self, _value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
panic!("Not Implemented")
}
fn serialize_entry<K: ?Sized, V: ?Sized>(&mut self, key: &K, value: &V) -> Result<()>
where
K: Serialize,
V: Serialize,
{
let key_term = key.serialize(&Serializer {})?;
let value_term = value.serialize(&Serializer {})?;
self.items.push((key_term, value_term));
Ok(())
}
fn end(self) -> Result<Term> {
Ok(Term::Map(eetf::Map {
entries: self.items,
}))
}
}
impl<'a> ser::SerializeStruct for MapSerializer {
type Ok = Term;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let value_term = value.serialize(&Serializer {})?;
self.items
.push((Term::Atom(eetf::Atom::from(key)), value_term));
Ok(())
}
fn end(self) -> Result<Term> {
Ok(Term::Map(eetf::Map {
entries: self.items,
}))
}
}
impl<'a> ser::SerializeStructVariant for NamedMapSerializer {
type Ok = Term;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
where
T: ?Sized + Serialize,
{
let value_term = value.serialize(&Serializer {})?;
self.items
.push((Term::Atom(eetf::Atom::from(key)), value_term));
Ok(())
}
fn end(self) -> Result<Term> {
let serialized_data = Term::Map(eetf::Map {
entries: self.items,
});
Ok(Term::Tuple(eetf::Tuple::from(vec![
self.name,
serialized_data,
])))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn serialize_and_decode<T>(data: T) -> Term
where
T: Serialize,
{
let bytes = to_bytes(&data).expect("serialize failed");
Term::decode(io::Cursor::new(bytes)).expect("Decode failed")
}
#[test]
fn test_unsigned_ints_and_structs() {
#[derive(PartialEq, Serialize)]
struct TestStruct {
unsigned8: u8,
unsigned16: u16,
unsigned32: u32,
unsigned64: u64,
}
let result = serialize_and_decode(TestStruct {
unsigned8: 129,
unsigned16: 65530,
unsigned32: 65530,
unsigned64: 65530,
});
assert_eq!(
result,
Term::Map(eetf::Map::from(vec![
(
Term::Atom(eetf::Atom::from("unsigned8")),
Term::FixInteger(eetf::FixInteger::from(129))
),
(
Term::Atom(eetf::Atom::from("unsigned16")),
Term::FixInteger(eetf::FixInteger::from(65530))
),
(
Term::Atom(eetf::Atom::from("unsigned32")),
Term::BigInteger(eetf::BigInteger::from(65530))
),
(
Term::Atom(eetf::Atom::from("unsigned64")),
Term::BigInteger(eetf::BigInteger::from(65530))
)
]))
)
}
#[test]
fn test_signed_ints_and_tuple_structs() {
#[derive(PartialEq, Serialize)]
struct TestStruct(i8, i16, i32, i64);
let result = serialize_and_decode(TestStruct(-127, 30000, 65530, 65530));
assert_eq!(
result,
Term::Tuple(eetf::Tuple::from(vec![
Term::FixInteger(eetf::FixInteger::from(-127)),
Term::FixInteger(eetf::FixInteger::from(30000)),
Term::FixInteger(eetf::FixInteger::from(65530)),
Term::BigInteger(eetf::BigInteger::from(65530)),
]))
)
}
#[test]
fn test_binaries_tuples_and_lists() {
let result = serialize_and_decode(("ABCD", vec![0, 1, 2]));
assert_eq!(
result,
Term::Tuple(eetf::Tuple::from(vec![
Term::Binary(eetf::Binary::from("ABCD".as_bytes())),
Term::List(eetf::List::from(vec![
Term::FixInteger(eetf::FixInteger::from(0)),
Term::FixInteger(eetf::FixInteger::from(1)),
Term::FixInteger(eetf::FixInteger::from(2)),
]))
]))
)
}
#[test]
fn test_option() {
let none: Option<u8> = None;
let nil_result = serialize_and_decode(none);
let some_result = serialize_and_decode(Some(0));
assert_eq!(nil_result, Term::Atom(eetf::Atom::from("nil")));
assert_eq!(some_result, Term::FixInteger(eetf::FixInteger::from(0)));
}
#[test]
fn test_unit_variant() {
#[derive(Serialize)]
enum E {
AnOption,
AnotherOption,
};
let result = serialize_and_decode(E::AnOption);
assert_eq!(result, Term::Atom(eetf::Atom::from("an_option")))
}
#[test]
fn test_newtype_variant() {
#[derive(Serialize)]
enum ErlResult {
Ok(String),
};
let result = serialize_and_decode(ErlResult::Ok("test".to_string()));
assert_eq!(
result,
Term::Tuple(eetf::Tuple::from(vec![
Term::Atom(eetf::Atom::from("ok")),
Term::Binary(eetf::Binary::from("test".as_bytes())),
]))
);
}
#[test]
fn test_tuple_variant() {
#[derive(Serialize)]
enum Testing {
Ok(u8, u8),
};
let result = serialize_and_decode(Testing::Ok(1, 2));
assert_eq!(
result,
Term::Tuple(eetf::Tuple::from(vec![
Term::Atom(eetf::Atom::from("ok")),
Term::Tuple(eetf::Tuple::from(vec![
Term::FixInteger(eetf::FixInteger::from(1)),
Term::FixInteger(eetf::FixInteger::from(2)),
]))
]))
);
}
}