Skip to content
Merged
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this
project adheres to [Semantic Versioning](http://semver.org/).

### Unreleased
- Add support for map (de)serialization.

## [0.4.1] - 2022-05-05

### Changed
Expand Down
104 changes: 84 additions & 20 deletions src/de/map.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use serde::de::{self, Visitor};

use crate::de::{Deserializer, Error};
use serde::de::{self, Visitor};

pub struct MapAccess<'a, 'b> {
de: &'a mut Deserializer<'b>,
Expand All @@ -13,6 +12,56 @@ impl<'a, 'b> MapAccess<'a, 'b> {
}
}

macro_rules! deserialize_signed_key {
($self:ident, $visitor:ident, $ixx:ident, $visit_ixx:ident) => {{
let de = $self.de;
match de.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
b'"' => de.eat_char(),
_ => return Err(Error::InvalidType),
};

let result = match de.peek() {
// after rust merged or-patterns feature, these two clause can be merged.
// error[E0658]: or-patterns syntax is experimental
Some(b'0'..=b'9') => super::deserialize_signed!(de, $visitor, $ixx, $visit_ixx),
Some(b'-') => super::deserialize_signed!(de, $visitor, $ixx, $visit_ixx),
_ => return Err(Error::InvalidType),
};
match de.peek() {
Some(b'"') => {
de.eat_char();
result
}
_ => Err(Error::InvalidType),
}
}};
}

macro_rules! deserialize_unsigned_key {
($self:ident, $visitor:ident, $ixx:ident, $visit_ixx:ident) => {{
let de = $self.de;
match de.parse_whitespace().ok_or(Error::EofWhileParsingValue)? {
b'"' => de.eat_char(),
_ => return Err(Error::InvalidType),
};

let result = match de.peek() {
// after rust merged or-patterns feature, these two clause can be merged.
// error[E0658]: or-patterns syntax is experimental
Some(b'0'..=b'9') => super::deserialize_unsigned!(de, $visitor, $ixx, $visit_ixx),
Some(b'-') => super::deserialize_unsigned!(de, $visitor, $ixx, $visit_ixx),
_ => return Err(Error::InvalidType),
};
match de.peek() {
Some(b'"') => {
de.eat_char();
result
}
_ => Err(Error::InvalidType),
}
}};
}

impl<'a, 'de> de::MapAccess<'de> for MapAccess<'a, 'de> {
type Error = Error;

Expand Down Expand Up @@ -79,60 +128,75 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> {
unreachable!()
}

fn deserialize_i8<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
deserialize_signed_key!(self, visitor, i8, visit_i8)
}

fn deserialize_i16<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
deserialize_signed_key!(self, visitor, i16, visit_i16)
}

fn deserialize_i32<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
deserialize_signed_key!(self, visitor, i32, visit_i32)
}

fn deserialize_i64<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
deserialize_signed_key!(self, visitor, i64, visit_i64)
}

fn deserialize_u8<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
// default implementation includes string unparsing
self.de.deserialize_i128(visitor)
}

fn deserialize_u16<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
deserialize_unsigned_key!(self, visitor, u8, visit_u8)
}

fn deserialize_u32<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
deserialize_unsigned_key!(self, visitor, u16, visit_u16)
}

fn deserialize_u64<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
deserialize_unsigned_key!(self, visitor, u32, visit_u32)
}

fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
deserialize_unsigned_key!(self, visitor, u64, visit_u64)
}

fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
self.de.deserialize_u128(visitor)
}

fn deserialize_f32<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
Expand Down Expand Up @@ -163,11 +227,11 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'a, 'de> {
self.de.deserialize_str(visitor)
}

fn deserialize_string<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
unreachable!()
self.de.deserialize_string(visitor)
}

fn deserialize_bytes<V>(self, _visitor: V) -> Result<V::Value, Self::Error>
Expand Down
126 changes: 110 additions & 16 deletions src/de/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ macro_rules! deserialize_unsigned {
}
}};
}
pub(crate) use deserialize_unsigned;

macro_rules! deserialize_signed {
($self:ident, $visitor:ident, $ixx:ident, $visit_ixx:ident) => {{
Expand Down Expand Up @@ -245,6 +246,7 @@ macro_rules! deserialize_signed {
}
}};
}
pub(crate) use deserialize_signed;

impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
type Error = Error;
Expand Down Expand Up @@ -576,22 +578,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
self.deserialize_seq(visitor)
}

/// Unsupported. Can’t make an arbitrary-sized map in no-std. Use a struct with a
/// known format, or implement a custom map deserializer / visitor:
/// https://serde.rs/deserialize-map.html
fn deserialize_map<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
unreachable!()
}

fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
Expand All @@ -610,6 +597,18 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> {
}
}

fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_map(visitor)
}

fn deserialize_enum<V>(
self,
_name: &'static str,
Expand Down Expand Up @@ -1097,6 +1096,101 @@ mod tests {
);
}

#[test]
fn numbered_key_maps() {
use std::collections::BTreeMap;

// u8
let mut ranking: BTreeMap<u8, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<u8, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);

// u16
let mut ranking: BTreeMap<u16, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<u16, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);

// u32
let mut ranking: BTreeMap<u32, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<u32, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);

// u64
let mut ranking: BTreeMap<u64, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<u64, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);

// u128
let mut ranking: BTreeMap<u128, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<u128, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);

// i8
let mut ranking: BTreeMap<i8, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<i8, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);

// i16
let mut ranking: BTreeMap<i16, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<i16, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);

// i32
let mut ranking: BTreeMap<i32, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<i32, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);

// i64
let mut ranking: BTreeMap<i64, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<i64, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);

// i128
let mut ranking: BTreeMap<i128, String> = BTreeMap::new();
ranking.insert(1, "Elon".to_string());
ranking.insert(2, "Bazos".to_string());
assert_eq!(
from_str::<BTreeMap<i128, String>>(r#"{"1": "Elon", "2": "Bazos"}"#).unwrap(),
ranking
);
}

#[test]
fn deserialize_optional_vector() {
#[derive(Debug, Deserialize, PartialEq)]
Expand Down
11 changes: 11 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ pub use self::ser::{to_string, to_vec};

#[cfg(test)]
mod test {
use std::collections::BTreeMap;

use super::*;
use serde_derive::{Deserialize, Serialize};

Expand Down Expand Up @@ -95,6 +97,7 @@ mod test {
published: bool,
comments: Vec<CommentId>,
stats: Stats,
balances: BTreeMap<String, u16>,
}

#[test]
Expand All @@ -107,7 +110,10 @@ mod test {
published: false,
comments: vec![],
stats: Stats { views: 0, score: 0 },
balances: BTreeMap::new(),
};
let mut balances: BTreeMap<String, u16> = BTreeMap::new();
balances.insert("chareen".into(), 347);
let max = Item {
model: Model::Post {
category: "fun".to_string(),
Expand All @@ -122,6 +128,7 @@ mod test {
views: std::u64::MAX,
score: std::i64::MIN,
},
balances,
};

// binary
Expand Down Expand Up @@ -172,6 +179,9 @@ mod test {
author: Address("no-reply@domain.com".to_owned()),
});

let mut balances: BTreeMap<String, u16> = BTreeMap::new();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, so this test will show parse(serialize(x)) == x right?

And does work with u16 as tree value, which is promising.

Maybe other pieces already work. Can you add test cases for other types?

Also, this only works when the map has all the same type of value, right? I guess we need some more work for flatten, but we can leave that for another PR.

balances.insert("chareen".into(), 347);

let item = ModelOrItem::Item(Item {
model: Model::Comment,
title: "Title".to_owned(),
Expand All @@ -183,6 +193,7 @@ mod test {
views: 110,
score: 12,
},
balances,
});

assert_eq!(
Expand Down
Loading