#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
#![doc(test(attr(deny(warnings))))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[doc = include_str!("../README.md")]
#[cfg(all(doctest, feature = "std"))]
pub struct ReadmeDoctests;
#[cfg(test)]
mod tests;
extern crate alloc;
#[cfg(feature = "json-schema")]
use alloc::borrow::Cow;
use alloc::collections::BTreeMap;
use core::{fmt, marker::PhantomData};
#[cfg(feature = "std")]
use std::{
collections::{hash_map::RandomState, HashMap},
hash::{BuildHasher, Hash},
};
#[cfg(feature = "json-schema")]
use schemars::{json_schema, JsonSchema, Schema, SchemaGenerator};
use serde::{
de::{Error as SerdeError, MapAccess, SeqAccess, Visitor},
ser::{SerializeStruct, Serializer},
Deserialize, Deserializer, Serialize,
};
pub struct BTreeMapToArray<K, V, N = DefaultLabels, T = BTreeMap<K, V>>(PhantomData<(K, V, N, T)>);
impl<K, V, N, T> BTreeMapToArray<K, V, N, T> {
pub fn serialize<'a, S>(map: &'a T, serializer: S) -> Result<S::Ok, S::Error>
where
K: 'a + Serialize,
V: 'a + Serialize,
N: KeyValueLabels,
&'a T: IntoIterator<Item = (&'a K, &'a V)> + Serialize,
S: Serializer,
{
serialize::<K, V, N, T, S>(map, serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<T, D::Error>
where
K: Deserialize<'de> + Ord,
V: Deserialize<'de>,
N: KeyValueLabels,
BTreeMap<K, V>: Into<T>,
T: Deserialize<'de>,
D: Deserializer<'de>,
{
let map = if deserializer.is_human_readable() {
deserializer.deserialize_seq(BTreeMapToArrayVisitor::<K, V, N>(PhantomData))
} else {
BTreeMap::<K, V>::deserialize(deserializer)
}?;
Ok(map.into())
}
}
#[cfg(feature = "json-schema")]
impl<K, V, N, T> JsonSchema for BTreeMapToArray<K, V, N, T>
where
K: JsonSchema,
V: JsonSchema,
N: KeyValueJsonSchema,
{
fn schema_name() -> Cow<'static, str> {
Vec::<KeyValue<K, V, N>>::schema_name()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
Vec::<KeyValue<K, V, N>>::json_schema(generator)
}
}
fn serialize<'a, K, V, N, T, S>(map: &'a T, serializer: S) -> Result<S::Ok, S::Error>
where
K: 'a + Serialize,
V: 'a + Serialize,
N: KeyValueLabels,
&'a T: IntoIterator<Item = (&'a K, &'a V)> + Serialize,
S: Serializer,
{
if serializer.is_human_readable() {
serializer.collect_seq(map.into_iter().map(|(key, value)| KeyValue {
key,
value,
_phantom: PhantomData::<N>,
}))
} else {
map.serialize(serializer)
}
}
pub trait KeyValueLabels {
const KEY: &'static str;
const VALUE: &'static str;
}
#[cfg(feature = "json-schema")]
pub trait KeyValueJsonSchema: KeyValueLabels {
const JSON_SCHEMA_KV_NAME: Option<&'static str> = None;
const JSON_SCHEMA_KV_DESCRIPTION: Option<&'static str> = None;
const JSON_SCHEMA_KEY_DESCRIPTION: Option<&'static str> = None;
const JSON_SCHEMA_VALUE_DESCRIPTION: Option<&'static str> = None;
}
pub struct DefaultLabels;
impl KeyValueLabels for DefaultLabels {
const KEY: &'static str = "key";
const VALUE: &'static str = "value";
}
#[cfg(feature = "json-schema")]
impl KeyValueJsonSchema for DefaultLabels {}
struct KeyValue<K, V, N> {
key: K,
value: V,
_phantom: PhantomData<N>,
}
impl<K, V, N> Serialize for KeyValue<K, V, N>
where
K: Serialize,
V: Serialize,
N: KeyValueLabels,
{
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let mut state = serializer.serialize_struct("KeyValue", 2)?;
state.serialize_field(N::KEY, &self.key)?;
state.serialize_field(N::VALUE, &self.value)?;
state.end()
}
}
impl<K, V, N: KeyValueLabels> KeyValue<K, V, N> {
const FIELDS: &'static [&'static str] = &[N::KEY, N::VALUE];
}
impl<'de, K, V, N> Deserialize<'de> for KeyValue<K, V, N>
where
K: Deserialize<'de>,
V: Deserialize<'de>,
N: KeyValueLabels,
{
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
enum Field<N: KeyValueLabels> {
Key,
Value(PhantomData<N>),
}
impl<'de, N: KeyValueLabels> Deserialize<'de> for Field<N> {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Field<N>, D::Error> {
struct FieldVisitor<N>(PhantomData<N>);
impl<'de, N: KeyValueLabels> Visitor<'de> for FieldVisitor<N> {
type Value = Field<N>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "`{}` or `{}`", N::KEY, N::VALUE)
}
fn visit_str<E: SerdeError>(self, value: &str) -> Result<Field<N>, E> {
if value == N::KEY {
Ok(Field::Key)
} else if value == N::VALUE {
Ok(Field::Value(PhantomData))
} else {
Err(SerdeError::unknown_field(value, &[N::KEY, N::VALUE]))
}
}
}
deserializer.deserialize_identifier(FieldVisitor(PhantomData))
}
}
struct KvVisitor<K, V, N>(PhantomData<(K, V, N)>);
impl<'de, K, V, N> Visitor<'de> for KvVisitor<K, V, N>
where
K: Deserialize<'de>,
V: Deserialize<'de>,
N: KeyValueLabels,
{
type Value = KeyValue<K, V, N>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("struct KeyValue")
}
fn visit_seq<SeqA: SeqAccess<'de>>(
self,
mut seq: SeqA,
) -> Result<KeyValue<K, V, N>, SeqA::Error> {
let key = seq
.next_element()?
.ok_or_else(|| SerdeError::invalid_length(0, &self))?;
let value = seq
.next_element()?
.ok_or_else(|| SerdeError::invalid_length(1, &self))?;
Ok(KeyValue {
key,
value,
_phantom: PhantomData,
})
}
fn visit_map<MapA: MapAccess<'de>>(
self,
mut map: MapA,
) -> Result<KeyValue<K, V, N>, MapA::Error> {
let mut ret_key = None;
let mut ret_value = None;
while let Some(key) = map.next_key::<Field<N>>()? {
match key {
Field::Key => {
if ret_key.is_some() {
return Err(SerdeError::duplicate_field(N::KEY));
}
ret_key = Some(map.next_value()?);
}
Field::Value(_) => {
if ret_value.is_some() {
return Err(SerdeError::duplicate_field(N::VALUE));
}
ret_value = Some(map.next_value()?);
}
}
}
let key = ret_key.ok_or_else(|| SerdeError::missing_field(N::KEY))?;
let value = ret_value.ok_or_else(|| SerdeError::missing_field(N::VALUE))?;
Ok(KeyValue {
key,
value,
_phantom: PhantomData,
})
}
}
deserializer.deserialize_struct("KeyValue", Self::FIELDS, KvVisitor::<_, _, N>(PhantomData))
}
}
#[cfg(feature = "json-schema")]
impl<K, V, N> JsonSchema for KeyValue<K, V, N>
where
K: JsonSchema,
V: JsonSchema,
N: KeyValueJsonSchema,
{
fn schema_name() -> Cow<'static, str> {
match N::JSON_SCHEMA_KV_NAME {
Some(name) => Cow::Borrowed(name),
None => Cow::Owned(format!(
"KeyValue_for_{}_and_{}",
K::schema_name(),
V::schema_name()
)),
}
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
let apply_description_if_set = |schema: &mut Schema, maybe_description: Option<&str>| {
if let Some(description) = maybe_description {
schema
.ensure_object()
.insert("description".into(), description.into());
}
};
let mut key_schema = generator.subschema_for::<K>();
apply_description_if_set(&mut key_schema, N::JSON_SCHEMA_KEY_DESCRIPTION);
let mut value_schema = generator.subschema_for::<V>();
apply_description_if_set(&mut value_schema, N::JSON_SCHEMA_VALUE_DESCRIPTION);
let mut schema: Schema = json_schema!({
"type": "object",
"properties": {
N::KEY: key_schema,
N::VALUE: value_schema,
}
});
apply_description_if_set(&mut schema, N::JSON_SCHEMA_KV_DESCRIPTION);
let required = schema
.ensure_object()
.entry("required")
.or_insert(serde_json::Value::Array(Vec::new()))
.as_array_mut()
.expect("`required` should be an array");
required.push(N::KEY.into());
required.push(N::VALUE.into());
schema
}
}
struct BTreeMapToArrayVisitor<K, V, N>(PhantomData<(K, V, N)>);
impl<'de, K, V, N> Visitor<'de> for BTreeMapToArrayVisitor<K, V, N>
where
K: Deserialize<'de> + Ord,
V: Deserialize<'de>,
N: KeyValueLabels,
{
type Value = BTreeMap<K, V>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a BTreeMapToArray")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut map = BTreeMap::new();
while let Some(entry) = seq.next_element::<KeyValue<K, V, N>>()? {
map.insert(entry.key, entry.value);
}
Ok(map)
}
}
#[cfg(feature = "std")]
pub struct HashMapToArray<K, V, N = DefaultLabels, U = RandomState, T = HashMap<K, V, U>>(
PhantomData<(K, V, N, U, T)>,
);
#[cfg(feature = "std")]
impl<K, V, N, U, T> HashMapToArray<K, V, N, U, T> {
pub fn serialize<'a, S>(map: &'a T, serializer: S) -> Result<S::Ok, S::Error>
where
K: 'a + Serialize,
V: 'a + Serialize,
N: KeyValueLabels,
&'a T: IntoIterator<Item = (&'a K, &'a V)> + Serialize,
S: Serializer,
{
serialize::<K, V, N, T, S>(map, serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<T, D::Error>
where
K: Deserialize<'de> + Eq + Hash,
V: Deserialize<'de>,
N: KeyValueLabels,
U: BuildHasher + Default,
HashMap<K, V, U>: Into<T>,
T: Deserialize<'de>,
D: Deserializer<'de>,
{
let map = if deserializer.is_human_readable() {
deserializer.deserialize_seq(HashMapToArrayVisitor::<K, V, N, U>(PhantomData))
} else {
HashMap::<K, V, U>::deserialize(deserializer)
}?;
Ok(map.into())
}
}
#[cfg(feature = "json-schema")]
impl<K, V, N, U, T> JsonSchema for HashMapToArray<K, V, N, U, T>
where
K: JsonSchema,
V: JsonSchema,
N: KeyValueJsonSchema,
{
fn schema_name() -> Cow<'static, str> {
Vec::<KeyValue<K, V, N>>::schema_name()
}
fn json_schema(generator: &mut SchemaGenerator) -> Schema {
Vec::<KeyValue<K, V, N>>::json_schema(generator)
}
}
#[cfg(feature = "std")]
struct HashMapToArrayVisitor<K, V, N, U>(PhantomData<(K, V, N, U)>);
#[cfg(feature = "std")]
impl<'de, K, V, N, U> Visitor<'de> for HashMapToArrayVisitor<K, V, N, U>
where
K: Deserialize<'de> + Eq + Hash,
V: Deserialize<'de>,
N: KeyValueLabels,
U: BuildHasher + Default,
{
type Value = HashMap<K, V, U>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a HashMapToArray")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut map = HashMap::<K, V, U>::default();
while let Some(entry) = seq.next_element::<KeyValue<K, V, N>>()? {
map.insert(entry.key, entry.value);
}
Ok(map)
}
}