use std::fmt;
use std::marker::PhantomData;
use serde::de::{self, Deserializer, MapAccess, Visitor};
use serde::ser::{SerializeMap, Serializer};
use serde::{Deserialize, Serialize};
pub(crate) trait Keyed {
fn key(&self) -> &str;
fn set_key(&mut self, key: String);
}
pub(crate) fn deserialize<'de, D, T>(de: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Keyed,
{
struct KeyedVisitor<T>(PhantomData<T>);
impl<'de, T> Visitor<'de> for KeyedVisitor<T>
where
T: Deserialize<'de> + Keyed,
{
type Value = Vec<T>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a map of named entries")
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let mut out = Vec::with_capacity(map.size_hint().unwrap_or(0));
while let Some(key) = map.next_key::<String>()? {
let mut value: T = map.next_value()?;
value.set_key(key);
out.push(value);
}
Ok(out)
}
fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
Ok(Vec::new())
}
}
de.deserialize_map(KeyedVisitor(PhantomData))
}
pub(crate) fn serialize<S, T>(items: &[T], ser: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize + Keyed,
{
let mut map = ser.serialize_map(Some(items.len()))?;
for item in items {
map.serialize_entry(item.key(), item)?;
}
map.end()
}
pub(crate) fn split_entry<'de, A, F>(mut map: A, mut own: F) -> Result<serde_json::Value, A::Error>
where
A: MapAccess<'de>,
F: FnMut(&str, &mut A) -> Result<bool, A::Error>,
{
let mut theirs = serde_json::Map::new();
while let Some(key) = map.next_key::<String>()? {
if !own(&key, &mut map)? {
let value: serde_json::Value = map.next_value()?;
theirs.insert(key, value);
}
}
if matches!(theirs.get("options"), Some(serde_json::Value::Object(_))) {
return Err(de::Error::custom(
"`options: { .. }` no longer nests a type's keys: write them on the entry itself",
));
}
Ok(if theirs.is_empty() {
serde_json::Value::Null
} else {
serde_json::Value::Object(theirs)
})
}
pub(crate) fn serialize_theirs<M: SerializeMap>(
map: &mut M,
options: &serde_json::Value,
) -> Result<(), M::Error> {
match options {
serde_json::Value::Object(entries) => {
for (key, value) in entries {
map.serialize_entry(key, value)?;
}
}
serde_json::Value::Null => {}
other => map.serialize_entry("options", other)?,
}
Ok(())
}