Add serde serializer
This commit is contained in:
parent
4f354768b6
commit
4b8555711e
9 changed files with 630 additions and 37 deletions
|
@ -16,6 +16,7 @@ base64 = "0.13"
|
||||||
blake2 = { version = "0.10", optional = true }
|
blake2 = { version = "0.10", optional = true }
|
||||||
rand = "0.7"
|
rand = "0.7"
|
||||||
ed25519-dalek = { version = "1.0", optional = true }
|
ed25519-dalek = { version = "1.0", optional = true }
|
||||||
|
serde = { version = "1.0", optional = true, features = ["derive"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [ "blake2", "ed25519-dalek" ]
|
default = [ "blake2", "ed25519-dalek", "serde" ]
|
||||||
|
|
34
src/dec/error.rs
Normal file
34
src/dec/error.rs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// The type of errors returned by helper functions on `Term`
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TypeError {
|
||||||
|
/// The term could not be decoded in the given type
|
||||||
|
WrongType(&'static str),
|
||||||
|
/// The term is not an array of the requested length
|
||||||
|
WrongLength(usize, usize),
|
||||||
|
/// The dictionnary is missing a key
|
||||||
|
MissingKey(String),
|
||||||
|
/// The dictionnary contains an invalid key
|
||||||
|
UnexpectedKey(String),
|
||||||
|
/// The underlying raw string contains garbage (should not happen in theory)
|
||||||
|
Garbage,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::str::Utf8Error> for TypeError {
|
||||||
|
fn from(_x: std::str::Utf8Error) -> TypeError {
|
||||||
|
TypeError::Garbage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for TypeError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
TypeError::WrongType(t) => write!(f, "Not a {}", t),
|
||||||
|
TypeError::WrongLength(n, m) => write!(f, "Expected {} items, got {}", m, n),
|
||||||
|
TypeError::MissingKey(k) => write!(f, "Missing key `{}` in dict", k),
|
||||||
|
TypeError::UnexpectedKey(k) => write!(f, "Spurrious/unexpected key `{}` in dict", k),
|
||||||
|
TypeError::Garbage => write!(f, "Garbage in underlying data"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,13 @@
|
||||||
//! Functions to decode nettext and helpers to map it to data structures
|
//! Functions to decode nettext and helpers to map it to data structures
|
||||||
|
|
||||||
mod decode;
|
mod decode;
|
||||||
|
mod error;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::crypto;
|
use crate::crypto;
|
||||||
pub use decode::*;
|
pub use decode::*;
|
||||||
|
pub use error::TypeError;
|
||||||
|
|
||||||
/// A parsed nettext term, with many helpers for destructuring
|
/// A parsed nettext term, with many helpers for destructuring
|
||||||
///
|
///
|
||||||
|
@ -63,27 +65,6 @@ impl<'a> From<AnyTerm<'a, 'static>> for Term<'a, 'static> {
|
||||||
|
|
||||||
// ---- PUBLIC IMPLS ----
|
// ---- PUBLIC IMPLS ----
|
||||||
|
|
||||||
/// The type of errors returned by helper functions on `Term`
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum TypeError {
|
|
||||||
/// The term could not be decoded in the given type
|
|
||||||
WrongType(&'static str),
|
|
||||||
/// The term is not an array of the requested length
|
|
||||||
WrongLength(usize, usize),
|
|
||||||
/// The dictionnary is missing a key
|
|
||||||
MissingKey(String),
|
|
||||||
/// The dictionnary contains an invalid key
|
|
||||||
UnexpectedKey(String),
|
|
||||||
/// The underlying raw string contains garbage (should not happen in theory)
|
|
||||||
Garbage,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::str::Utf8Error> for TypeError {
|
|
||||||
fn from(_x: std::str::Utf8Error) -> TypeError {
|
|
||||||
TypeError::Garbage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> Term<'a, 'b> {
|
impl<'a, 'b> Term<'a, 'b> {
|
||||||
// ---- STRUCTURAL MAPPINGS ----
|
// ---- STRUCTURAL MAPPINGS ----
|
||||||
|
|
||||||
|
|
21
src/enc/error.rs
Normal file
21
src/enc/error.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// An error that happenned when creating a nettext encoder term
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
InvalidCharacter(u8),
|
||||||
|
InvalidRaw,
|
||||||
|
NotADictionnary,
|
||||||
|
ListInList,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::InvalidCharacter(c) => write!(f, "Invalid character '{}'", *c as char),
|
||||||
|
Error::InvalidRaw => write!(f, "Invalid RAW nettext litteral"),
|
||||||
|
Error::NotADictionnary => write!(f, "Tried to insert into a term that isn't a dictionnary"),
|
||||||
|
Error::ListInList => write!(f, "Refusing to build nested lists with list(), use either list_flatten() or list_nested()"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,30 +17,26 @@
|
||||||
//! ]).unwrap().encode();
|
//! ]).unwrap().encode();
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::borrow::{Cow, Borrow};
|
||||||
|
|
||||||
use crate::dec::{self, decode};
|
use crate::dec::{self, decode};
|
||||||
use crate::{is_string_char, is_whitespace};
|
use crate::{is_string_char, is_whitespace};
|
||||||
|
|
||||||
|
pub use error::Error;
|
||||||
|
|
||||||
/// A term meant to be encoded into a nettext representation
|
/// A term meant to be encoded into a nettext representation
|
||||||
pub struct Term<'a>(T<'a>);
|
pub struct Term<'a>(T<'a>);
|
||||||
|
|
||||||
enum T<'a> {
|
enum T<'a> {
|
||||||
Str(&'a [u8]),
|
Str(&'a [u8]),
|
||||||
OwnedStr(Vec<u8>),
|
OwnedStr(Vec<u8>),
|
||||||
Dict(HashMap<&'a [u8], T<'a>>),
|
Dict(HashMap<Cow<'a, [u8]>, T<'a>>),
|
||||||
List(Vec<T<'a>>),
|
List(Vec<T<'a>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An error that happenned when creating a nettext encoder term
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Error {
|
|
||||||
InvalidCharacter(u8),
|
|
||||||
InvalidRaw,
|
|
||||||
NotADictionnary,
|
|
||||||
ListInList,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<'a> = std::result::Result<Term<'a>, Error>;
|
pub type Result<'a> = std::result::Result<Term<'a>, Error>;
|
||||||
|
|
||||||
// ---- helpers to transform datatypes into encoder terms ----
|
// ---- helpers to transform datatypes into encoder terms ----
|
||||||
|
@ -74,6 +70,16 @@ pub fn string(s: &str) -> Result<'_> {
|
||||||
Ok(Term(T::Str(s.as_bytes())))
|
Ok(Term(T::Str(s.as_bytes())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same as `string` but takes an owned String
|
||||||
|
pub fn string_owned(s: String) -> Result<'static> {
|
||||||
|
for c in s.as_bytes().iter() {
|
||||||
|
if !(is_string_char(*c) || is_whitespace(*c)) {
|
||||||
|
return Err(Error::InvalidCharacter(*c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Term(T::OwnedStr(s.into_bytes())))
|
||||||
|
}
|
||||||
|
|
||||||
/// Include a raw nettext value
|
/// Include a raw nettext value
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -88,6 +94,13 @@ pub fn raw(bytes: &[u8]) -> Result<'_> {
|
||||||
Ok(Term(T::Str(bytes)))
|
Ok(Term(T::Str(bytes)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn safe_raw(bytes: &[u8]) -> Term<'_> {
|
||||||
|
Term(T::Str(bytes))
|
||||||
|
}
|
||||||
|
pub(crate) fn safe_raw_owned(bytes: Vec<u8>) -> Term<'static> {
|
||||||
|
Term(T::OwnedStr(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
/// Term corresponding to a list of terms
|
/// Term corresponding to a list of terms
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -109,6 +122,30 @@ pub fn list<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
|
||||||
Ok(Term(T::List(tmp)))
|
Ok(Term(T::List(tmp)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Term corresponding to a list of terms. Sub-lists are flattenned.
|
||||||
|
pub fn list_flatten<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
|
||||||
|
let mut tmp = Vec::with_capacity(8);
|
||||||
|
for t in terms {
|
||||||
|
match t.0 {
|
||||||
|
T::List(t) => tmp.extend(t),
|
||||||
|
x => tmp.push(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Term(T::List(tmp)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Term corresponding to a list of terms. Sub-lists are represented as NESTED: `{ . = sub list items }`.
|
||||||
|
pub fn list_nested<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
|
||||||
|
let mut tmp = Vec::with_capacity(8);
|
||||||
|
for t in terms {
|
||||||
|
match t.0 {
|
||||||
|
T::List(t) => tmp.push(Term(T::List(t)).nested().0),
|
||||||
|
x => tmp.push(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Term(T::List(tmp)))
|
||||||
|
}
|
||||||
|
|
||||||
/// Term corresponding to a dictionnary of items
|
/// Term corresponding to a dictionnary of items
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -122,7 +159,15 @@ pub fn list<'a, I: IntoIterator<Item = Term<'a>>>(terms: I) -> Result<'a> {
|
||||||
pub fn dict<'a, I: IntoIterator<Item = (&'a str, Term<'a>)>>(pairs: I) -> Term<'a> {
|
pub fn dict<'a, I: IntoIterator<Item = (&'a str, Term<'a>)>>(pairs: I) -> Term<'a> {
|
||||||
let mut tmp = HashMap::new();
|
let mut tmp = HashMap::new();
|
||||||
for (k, v) in pairs {
|
for (k, v) in pairs {
|
||||||
tmp.insert(k.as_bytes(), v.0);
|
tmp.insert(Cow::from(k.as_bytes()), v.0);
|
||||||
|
}
|
||||||
|
Term(T::Dict(tmp))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn dict_owned_u8<'a, I: IntoIterator<Item = (Vec<u8>, Term<'a>)>>(pairs: I) -> Term<'a> {
|
||||||
|
let mut tmp = HashMap::new();
|
||||||
|
for (k, v) in pairs {
|
||||||
|
tmp.insert(Cow::from(k), v.0);
|
||||||
}
|
}
|
||||||
Term(T::Dict(tmp))
|
Term(T::Dict(tmp))
|
||||||
}
|
}
|
||||||
|
@ -178,7 +223,7 @@ impl<'a> Term<'a> {
|
||||||
pub fn insert(self, k: &'a str, v: Term<'a>) -> Result<'a> {
|
pub fn insert(self, k: &'a str, v: Term<'a>) -> Result<'a> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
T::Dict(mut d) => {
|
T::Dict(mut d) => {
|
||||||
d.insert(k.as_bytes(), v.0);
|
d.insert(Cow::from(k.as_bytes()), v.0);
|
||||||
Ok(Term(T::Dict(d)))
|
Ok(Term(T::Dict(d)))
|
||||||
}
|
}
|
||||||
_ => Err(Error::NotADictionnary),
|
_ => Err(Error::NotADictionnary),
|
||||||
|
@ -223,7 +268,7 @@ impl<'a> T<'a> {
|
||||||
} else if d.len() == 1 {
|
} else if d.len() == 1 {
|
||||||
buf.extend_from_slice(b"{ ");
|
buf.extend_from_slice(b"{ ");
|
||||||
let (k, v) = d.into_iter().next().unwrap();
|
let (k, v) = d.into_iter().next().unwrap();
|
||||||
buf.extend_from_slice(k);
|
buf.extend_from_slice(k.borrow());
|
||||||
buf.extend_from_slice(b" = ");
|
buf.extend_from_slice(b" = ");
|
||||||
v.encode_aux(buf, indent + 2, false);
|
v.encode_aux(buf, indent + 2, false);
|
||||||
buf.extend_from_slice(b" }");
|
buf.extend_from_slice(b" }");
|
||||||
|
@ -233,11 +278,11 @@ impl<'a> T<'a> {
|
||||||
let mut keys = d.keys().cloned().collect::<Vec<_>>();
|
let mut keys = d.keys().cloned().collect::<Vec<_>>();
|
||||||
keys.sort();
|
keys.sort();
|
||||||
for k in keys {
|
for k in keys {
|
||||||
let v = d.remove(k).unwrap();
|
let v = d.remove(&k).unwrap();
|
||||||
for _ in 0..indent2 {
|
for _ in 0..indent2 {
|
||||||
buf.push(b' ');
|
buf.push(b' ');
|
||||||
}
|
}
|
||||||
buf.extend_from_slice(k);
|
buf.extend_from_slice(k.borrow());
|
||||||
buf.extend_from_slice(b" = ");
|
buf.extend_from_slice(b" = ");
|
||||||
v.encode_aux(buf, indent2, false);
|
v.encode_aux(buf, indent2, false);
|
||||||
buf.extend_from_slice(b",\n");
|
buf.extend_from_slice(b",\n");
|
||||||
|
|
|
@ -85,6 +85,9 @@ pub mod crypto;
|
||||||
pub mod dec;
|
pub mod dec;
|
||||||
pub mod enc;
|
pub mod enc;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
pub mod serde;
|
||||||
|
|
||||||
// ---- syntactic elements of the data format ----
|
// ---- syntactic elements of the data format ----
|
||||||
|
|
||||||
pub(crate) const DICT_OPEN: u8 = b'{';
|
pub(crate) const DICT_OPEN: u8 = b'{';
|
||||||
|
|
51
src/serde/error.rs
Normal file
51
src/serde/error.rs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
use std;
|
||||||
|
use std::fmt::{self, Display};
|
||||||
|
|
||||||
|
use serde::{de, ser};
|
||||||
|
|
||||||
|
use crate::{dec, enc};
|
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Message(String),
|
||||||
|
Encode(enc::Error),
|
||||||
|
Type(dec::TypeError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<enc::Error> for Error {
|
||||||
|
fn from(e: enc::Error) -> Self {
|
||||||
|
Error::Encode(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<dec::TypeError> for Error {
|
||||||
|
fn from(e: dec::TypeError) -> Self {
|
||||||
|
Error::Type(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ser::Error for Error {
|
||||||
|
fn custom<T: Display>(msg: T) -> Self {
|
||||||
|
Error::Message(msg.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl de::Error for Error {
|
||||||
|
fn custom<T: Display>(msg: T) -> Self {
|
||||||
|
Error::Message(msg.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Error {
|
||||||
|
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::Message(msg) => formatter.write_str(msg),
|
||||||
|
Error::Encode(err) => write!(formatter, "Encode: {}", err),
|
||||||
|
Error::Type(err) => write!(formatter, "Type: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
7
src/serde/mod.rs
Normal file
7
src/serde/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
//mod de;
|
||||||
|
mod error;
|
||||||
|
mod ser;
|
||||||
|
|
||||||
|
//pub use de::{from_str, Deserializer};
|
||||||
|
pub use error::{Error, Result};
|
||||||
|
pub use ser::{to_bytes, Serializer};
|
450
src/serde/ser.rs
Normal file
450
src/serde/ser.rs
Normal file
|
@ -0,0 +1,450 @@
|
||||||
|
use serde::{ser, Serialize};
|
||||||
|
|
||||||
|
use crate::enc::*;
|
||||||
|
use crate::serde::error::{Error, Result};
|
||||||
|
use serde::ser::Error as SerError;
|
||||||
|
|
||||||
|
pub struct Serializer;
|
||||||
|
|
||||||
|
pub fn to_bytes<T>(value: &T) -> Result<Vec<u8>>
|
||||||
|
where
|
||||||
|
T: Serialize,
|
||||||
|
{
|
||||||
|
Ok(value.serialize(&mut Serializer)?.encode())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::Serializer for &'a mut Serializer {
|
||||||
|
type Ok = Term<'static>;
|
||||||
|
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
type SerializeSeq = SeqSerializer;
|
||||||
|
type SerializeTuple = SeqSerializer;
|
||||||
|
type SerializeTupleStruct = SeqSerializer;
|
||||||
|
type SerializeTupleVariant = SeqSerializer;
|
||||||
|
type SerializeMap = MapSerializer;
|
||||||
|
type SerializeStruct = StructSerializer;
|
||||||
|
type SerializeStructVariant = StructVariantSerializer;
|
||||||
|
|
||||||
|
fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
|
||||||
|
Ok(if v {
|
||||||
|
safe_raw(b"true")
|
||||||
|
} else {
|
||||||
|
safe_raw(b"false")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
|
||||||
|
self.serialize_i64(i64::from(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
|
||||||
|
self.serialize_i64(i64::from(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
|
||||||
|
self.serialize_i64(i64::from(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
|
||||||
|
Ok(safe_raw_owned(v.to_string().into_bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
|
||||||
|
self.serialize_u64(u64::from(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
|
||||||
|
self.serialize_u64(u64::from(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
|
||||||
|
self.serialize_u64(u64::from(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
|
||||||
|
Ok(safe_raw_owned(v.to_string().into_bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
|
||||||
|
self.serialize_f64(f64::from(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
|
||||||
|
Ok(string_owned(v.to_string())?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_char(self, v: char) -> Result<Self::Ok> {
|
||||||
|
self.serialize_str(&v.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_str(self, v: &str) -> Result<Self::Ok> {
|
||||||
|
Ok(bytes(v.as_bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
|
||||||
|
Ok(bytes(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_none(self) -> Result<Self::Ok> {
|
||||||
|
Ok(dict([]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
value.serialize(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_unit(self) -> Result<Self::Ok> {
|
||||||
|
Ok(dict([]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok> {
|
||||||
|
Ok(string(name)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_unit_variant(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
_variant_index: u32,
|
||||||
|
variant: &'static str,
|
||||||
|
) -> Result<Self::Ok> {
|
||||||
|
Ok(string_owned(format!("{}.{}", name, variant))?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_newtype_struct<T>(self, name: &'static str, value: &T) -> Result<Self::Ok>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
Ok(list_flatten([string(name)?, value.serialize(self)?])?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_newtype_variant<T>(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
_variant_index: u32,
|
||||||
|
variant: &'static str,
|
||||||
|
value: &T,
|
||||||
|
) -> Result<Self::Ok>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
Ok(list_flatten([
|
||||||
|
string_owned(format!("{}.{}", name, variant))?,
|
||||||
|
value.serialize(self)?,
|
||||||
|
])?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq> {
|
||||||
|
Ok(SeqSerializer { items: vec![] })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple> {
|
||||||
|
Ok(SeqSerializer { items: Vec::with_capacity(len) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_tuple_struct(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
len: usize,
|
||||||
|
) -> Result<Self::SerializeTupleStruct> {
|
||||||
|
let mut items = Vec::with_capacity(len + 1);
|
||||||
|
items.push(string(name)?);
|
||||||
|
Ok(SeqSerializer {
|
||||||
|
items,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_tuple_variant(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
_variant_index: u32,
|
||||||
|
variant: &'static str,
|
||||||
|
len: usize,
|
||||||
|
) -> Result<Self::SerializeTupleVariant> {
|
||||||
|
let mut items = Vec::with_capacity(len + 1);
|
||||||
|
items.push(string_owned(format!("{}.{}", name, variant))?);
|
||||||
|
Ok(SeqSerializer {
|
||||||
|
items,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
|
||||||
|
Ok(MapSerializer {
|
||||||
|
next: None,
|
||||||
|
fields: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct> {
|
||||||
|
Ok(StructSerializer {
|
||||||
|
name,
|
||||||
|
fields: Vec::with_capacity(len),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_struct_variant(
|
||||||
|
self,
|
||||||
|
name: &'static str,
|
||||||
|
_variant_index: u32,
|
||||||
|
variant: &'static str,
|
||||||
|
len: usize,
|
||||||
|
) -> Result<Self::SerializeStructVariant> {
|
||||||
|
Ok(StructVariantSerializer {
|
||||||
|
name,
|
||||||
|
variant,
|
||||||
|
fields: Vec::with_capacity(len),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -- sub-serializers --
|
||||||
|
|
||||||
|
pub struct SeqSerializer {
|
||||||
|
items: Vec<Term<'static>>,
|
||||||
|
}
|
||||||
|
impl<'a> ser::SerializeSeq for SeqSerializer {
|
||||||
|
type Ok = Term<'static>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
self.items.push(value.serialize(&mut Serializer)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok> {
|
||||||
|
Ok(list_nested(self.items.into_iter())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::SerializeTuple for SeqSerializer {
|
||||||
|
type Ok = Term<'static>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
self.items.push(value.serialize(&mut Serializer)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok> {
|
||||||
|
Ok(list_nested(self.items.into_iter())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::SerializeTupleStruct for SeqSerializer {
|
||||||
|
type Ok = Term<'static>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
self.items.push(value.serialize(&mut Serializer)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok> {
|
||||||
|
Ok(list_nested(self.items.into_iter())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::SerializeTupleVariant for SeqSerializer {
|
||||||
|
type Ok = Term<'static>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
self.items.push(value.serialize(&mut Serializer)?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Self::Ok> {
|
||||||
|
Ok(list_nested(self.items.into_iter())?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MapSerializer {
|
||||||
|
next: Option<Vec<u8>>,
|
||||||
|
fields: Vec<(Vec<u8>, Term<'static>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::SerializeMap for MapSerializer {
|
||||||
|
type Ok = Term<'static>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
self.next = Some(key.serialize(&mut Serializer)?.encode());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
self.fields.push((
|
||||||
|
self.next.take().ok_or(Self::Error::custom("no key"))?,
|
||||||
|
value.serialize(&mut Serializer)?,
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Term<'static>> {
|
||||||
|
Ok(dict_owned_u8(self.fields.into_iter()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StructSerializer {
|
||||||
|
name: &'static str,
|
||||||
|
fields: Vec<(&'static str, Term<'static>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ser::SerializeStruct for StructSerializer {
|
||||||
|
type Ok = Term<'static>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
self.fields.push((key, value.serialize(&mut Serializer)?));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Term<'static>> {
|
||||||
|
Ok(list([string(self.name)?, dict(self.fields.into_iter())])?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StructVariantSerializer {
|
||||||
|
name: &'static str,
|
||||||
|
variant: &'static str,
|
||||||
|
fields: Vec<(&'static str, Term<'static>)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ser::SerializeStructVariant for StructVariantSerializer {
|
||||||
|
type Ok = Term<'static>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<()>
|
||||||
|
where
|
||||||
|
T: ?Sized + Serialize,
|
||||||
|
{
|
||||||
|
self.fields.push((key, value.serialize(&mut Serializer)?));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn end(self) -> Result<Term<'static>> {
|
||||||
|
Ok(list([
|
||||||
|
string_owned(format!("{}.{}", self.name, self.variant))?,
|
||||||
|
dict(self.fields.into_iter()),
|
||||||
|
])?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_struct() {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Test {
|
||||||
|
int: u32,
|
||||||
|
seq: Vec<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let test = Test {
|
||||||
|
int: 1,
|
||||||
|
seq: vec!["a", "b"],
|
||||||
|
};
|
||||||
|
let expected = br#"Test {
|
||||||
|
int = 1,
|
||||||
|
seq = YQ Yg,
|
||||||
|
}"#;
|
||||||
|
assert_eq!(&to_bytes(&test).unwrap(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_enum() {
|
||||||
|
#[derive(Serialize)]
|
||||||
|
enum E {
|
||||||
|
Unit,
|
||||||
|
Newtype(u32),
|
||||||
|
Tuple(u32, u32),
|
||||||
|
Struct { a: u32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
let u = E::Unit;
|
||||||
|
let expected = br#"E.Unit"#;
|
||||||
|
assert_eq!(&to_bytes(&u).unwrap(), expected);
|
||||||
|
|
||||||
|
let n = E::Newtype(1);
|
||||||
|
let expected = br#"E.Newtype 1"#;
|
||||||
|
assert_eq!(&to_bytes(&n).unwrap(), expected);
|
||||||
|
|
||||||
|
let t = E::Tuple(1, 2);
|
||||||
|
let expected = br#"E.Tuple 1 2"#;
|
||||||
|
assert_eq!(&to_bytes(&t).unwrap(), expected);
|
||||||
|
|
||||||
|
let s = E::Struct { a: 1 };
|
||||||
|
let expected = br#"E.Struct { a = 1 }"#;
|
||||||
|
assert_eq!(&to_bytes(&s).unwrap(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_seq() {
|
||||||
|
let u = (1, 2, 3, 4);
|
||||||
|
let expected = br#"1 2 3 4"#;
|
||||||
|
assert_eq!(&to_bytes(&u).unwrap(), expected);
|
||||||
|
|
||||||
|
let n = (1, 2, (2, 3, 4), 5, 6);
|
||||||
|
let expected = br#"1 2 { . = 2 3 4 } 5 6"#;
|
||||||
|
assert_eq!(&to_bytes(&n).unwrap(), expected);
|
||||||
|
|
||||||
|
let t = [1, 2, 3, 4];
|
||||||
|
let expected = br#"1 2 3 4"#;
|
||||||
|
assert_eq!(&to_bytes(&t).unwrap(), expected);
|
||||||
|
|
||||||
|
let s = [[1, 2], [2, 3], [3, 4]];
|
||||||
|
let expected = br#"{ . = 1 2 } { . = 2 3 } { . = 3 4 }"#;
|
||||||
|
assert_eq!(&to_bytes(&s).unwrap(), expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_dict() {
|
||||||
|
let mut d = HashMap::new();
|
||||||
|
d.insert("hello", "world");
|
||||||
|
d.insert("dont", "panic");
|
||||||
|
let expected = br#"{
|
||||||
|
ZG9udA = cGFuaWM,
|
||||||
|
aGVsbG8 = d29ybGQ,
|
||||||
|
}"#;
|
||||||
|
assert_eq!(&to_bytes(&d).unwrap(), expected);
|
||||||
|
|
||||||
|
let mut d = HashMap::new();
|
||||||
|
d.insert(12, vec![42, 125]);
|
||||||
|
d.insert(33, vec![19, 22, 21]);
|
||||||
|
let expected = br#"{
|
||||||
|
12 = 42 125,
|
||||||
|
33 = 19 22 21,
|
||||||
|
}"#;
|
||||||
|
assert_eq!(&to_bytes(&d).unwrap(), expected);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue