use std::fmt;
use std::marker::PhantomData;
use serde::Deserializer;
use serde::de::{Error, MapAccess, SeqAccess, Visitor};
use serde_json::Value;
pub(crate) const NO_VARIANT: &str = "data did not match any variant of untagged enum ";
pub(crate) enum Shape {
Literal,
Unsigned(u64),
Signed(i64),
Float(f64),
Text(String),
List(Vec<Value>),
Object(Vec<(String, Value)>),
}
pub(crate) fn deserialize<'de, D: Deserializer<'de>, T>(
deserializer: D,
name: &'static str,
pick: fn(Shape) -> Option<T>,
) -> Result<T, D::Error> {
deserializer.deserialize_any(ShapeVisitor {
name,
pick,
output: PhantomData,
})
}
struct ShapeVisitor<T> {
name: &'static str,
pick: fn(Shape) -> Option<T>,
output: PhantomData<T>,
}
impl<T> ShapeVisitor<T> {
fn pick<E: Error>(&self, shape: Shape) -> Result<T, E> {
(self.pick)(shape).ok_or_else(|| E::custom(format_args!("{NO_VARIANT}{}", self.name)))
}
}
impl<'de, T> Visitor<'de> for ShapeVisitor<T> {
type Value = T;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(self.name)
}
fn visit_unit<E: Error>(self) -> Result<T, E> {
self.pick(Shape::Literal)
}
fn visit_bool<E: Error>(self, _: bool) -> Result<T, E> {
self.pick(Shape::Literal)
}
fn visit_u64<E: Error>(self, value: u64) -> Result<T, E> {
self.pick(Shape::Unsigned(value))
}
fn visit_i64<E: Error>(self, value: i64) -> Result<T, E> {
self.pick(Shape::Signed(value))
}
fn visit_f64<E: Error>(self, value: f64) -> Result<T, E> {
self.pick(Shape::Float(value))
}
fn visit_str<E: Error>(self, value: &str) -> Result<T, E> {
self.pick(Shape::Text(value.to_owned()))
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<T, A::Error> {
let mut items = Vec::new();
while let Some(item) = seq.next_element()? {
items.push(item);
}
self.pick(Shape::List(items))
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<T, A::Error> {
let mut members = Vec::new();
while let Some(member) = map.next_entry()? {
members.push(member);
}
self.pick(Shape::Object(members))
}
}