#map #sequence #deserializer #adapter #access #traits #move-oriented

serde-mobile

Move-oriented sequence and map access types for serde deserializers

4 stable releases

3.0.0 Apr 20, 2022
2.0.0 Apr 19, 2022
1.0.2 Apr 13, 2022

#2097 in Parser implementations


Used in kaydle

MPL-2.0 license

23KB
223 lines

serde-mobile

This crate provides traits and an adapter for creating move-oriented sequence and map accessors, as a complement to the remaining serde deserializer traits, which are entirely move-oriented. See docs.rs for details.


lib.rs:

This crate provides traits and an adapter for creating move-oriented sequence and map accessors, as a complement to the remaining serde deserializer traits, which are entirely move-oriented.

It provides SeqAccess, which replaces serde::de::SeqAccess, and MapKeyAccess / MapValueAccess, which collectively replace serde::de::MapAccess. These traits are often easier to use when implementing a Deserializer if you're focused on using by-move constructs, and help ensure correctness for callers.

In order to interoperate with serde, it also provides AccessAdapter. This struct takes any SeqAccess or MapKeyAccess type and converts it into a serde::de::SeqAccess or serde::de::MapAccess.

Example

In this example, we're interested in creating a deserializer that reads from an iterator of (key, value) pairs and emits them as a map. We create a a KeyAccess type, which implements MapKeyAccess. We use serde-mobile's built-in SubordinateValue type as our MapValueAccess, because we'll be using the common pattern where the iterator is stored by both the key and value accessor. This ends up being easier (if more verbose) to implement: a serde::de::MapAccess is a single type that needs to deserialize keys and values separately, and therefore needs some awkward design to capture the state where a key has been yielded but not a value. serde-mobile, on the other hand, splits this into a pair of types, so that only correct states can be expressed.

use std::collections::hash_map::{HashMap, IntoIter};
use std::marker::PhantomData;
use serde::de::{
IntoDeserializer,
Error,
DeserializeSeed,
value::{MapAccessDeserializer, Error as SimpleError},
};
use serde::Deserialize;
use serde_mobile::{
MapKeyAccess,
MapValueAccess,
AccessAdapter,
SubordinateValue
};

struct KeyAccess<I, E>{
entries: I,
error: PhantomData<E>,
}

impl<I, K, V, E> KeyAccess<I, E>
where
I: Iterator<Item=(K, V)>
{
fn new<C>(collection: C) -> Self
where C: IntoIterator<IntoIter=I>
{
Self {
entries: collection.into_iter(),
error: PhantomData,
}
}
}

// MapKeyAccess is the key-getting equivalent of serde::de::MapAccess
impl<'de, I, K, V, E> MapKeyAccess<'de> for KeyAccess<I, E>
where
I: Iterator<Item=(K, V)>,
K: IntoDeserializer<'de, E>,
V: IntoDeserializer<'de, E>,
E: Error,
{
type Error = E;
type Value = SubordinateValue<V::Deserializer, Self>;

// notice that next_key_seed takes self by move and returns Self::Value,
// which is a MapKeyAccess. This forces the caller to get a value before
// they can get another key.
fn next_key_seed<S>(mut self, seed: S) -> Result<Option<(S::Value, Self::Value)>, Self::Error>
where
S: DeserializeSeed<'de>
{
self.entries
.next()
.map(|(key, value)| {
seed
.deserialize(key.into_deserializer())
.map(|key| (
key,
SubordinateValue {
value: value.into_deserializer(),
parent: self
}
))
})
.transpose()
}

fn size_hint(&self) -> Option<usize> {
match self.entries.size_hint() {
(min, Some(max)) if min == max => Some(min),
_ => None,
}
}
}

// Normally we'd have to create a separate struct to implement `MapValueAccess`,
// but this pattern is common enough that serde-mobile provides a type called
// `SubordinateValue` that handles this pattern for us.

let serialized = HashMap::from([
("a", 10),
("b", 20),
]);

#[derive(Deserialize, Debug, PartialEq, Eq)]
struct Data {
a: i32,
b: i32,
}

let seq_access = KeyAccess::new(serialized);

// use an AccessAdapter to turn a serde-mobile access type
// into a serde access type
let deserializer = MapAccessDeserializer::new(AccessAdapter::new(seq_access));

match Data::deserialize(deserializer) {
Ok(data) => assert_eq!(data, Data { a: 10, b: 20 }),
Err(err) => {
let err: SimpleError = err;
panic!("failed to deserialize")
}
}

Dependencies

~110–350KB