use super::{reply::Reply, Response};
use frunk_core::hlist::{HCons, HNil};
use serde::{
de::{self, DeserializeOwned},
ser, Deserialize, Serialize,
};
use std::{
fmt,
marker::PhantomData,
ops::{Deref, DerefMut},
str::FromStr,
};
#[macro_export]
macro_rules! zoom {
($var:ident = $rec:ident . $field:ident) => {
#[allow(unused_mut)]
let (val, mut $rec) = $rec.pluck::<$crate::f![$field], _>();
let $var = val.into_inner();
};
(mut $var:ident = $rec:ident . $field:ident) => {
#[allow(unused_mut)]
let (val, mut $rec) = $rec.pluck::<$crate::f![$field], _>();
let mut $var = val.into_inner();
};
(&$rec:ident . $field:ident) => {
&**($rec.get::<$crate::f![$field], _>())
};
(&mut $rec:ident . $field:ident) => {
&mut **($rec.get_mut::<$crate::f![$field], _>())
};
}
#[macro_export]
macro_rules! R {
() => {
$crate::frunk_core::hlist::HNil
};
($name:ident : $type:ty) => {
$crate::frunk_core::hlist::HCons<
$crate::field::Field::<$type, { stringify!($name) }>,
$crate::frunk_core::hlist::HNil,
>
};
($name:ident : $type:ty , $( $tok:tt )*) => {
$crate::frunk_core::hlist::HCons<
$crate::field::Field::<$type, { stringify!($name) }>,
$crate::R![$( $tok )*],
>
};
($type:ty) => {
$crate::frunk_core::hlist::HCons<$type, $crate::frunk_core::hlist::HNil>
};
($type:ty , $( $tok:tt )*) => {
$crate::frunk_core::hlist::HCons<$type, $crate::R![$( $tok )*]>
};
(... $tail:ty) => {
$tail
}
}
#[macro_export]
macro_rules! r {
() => {
$crate::frunk_core::hlist::HNil
};
(... $tail:expr) => {
$tail
};
($name:ident = $value:expr) => {
$crate::frunk_core::hlist::HCons {
head: $crate::field::Field::<_, { stringify!($name) }>::new($value),
tail: $crate::frunk_core::hlist::HNil,
}
};
($name:ident = $value:expr , $( $tok:tt )*) => {
$crate::frunk_core::hlist::HCons {
head: $crate::field::Field::<_, { stringify!($name) }>::new($value),
tail: $crate::r![$( $tok )*],
}
};
($value:expr) => {
$crate::frunk_core::hlist::HCons {
head: $value,
tail: $crate::frunk_core::hlist::HNil,
}
};
($value:expr , $( $tok:tt )*) => {
$crate::frunk_core::hlist::HCons {
head: $value,
tail: $crate::r![$( $tok )*],
}
};
}
#[macro_export]
macro_rules! f {
($name:ident) => {
$crate::field::Field<_, { stringify!($name) }>
};
($name:ident : $type:ty) => {
$crate::field::Field<$type, { stringify!($name) }>
};
($name:ident = $val:expr) => {
$crate::field::Field::<_, { stringify!($name) }>::new($val)
};
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Field<T, const NAME: &'static str> {
inner: T,
}
impl<T, const NAME: &'static str> Field<T, NAME> {
#[inline]
pub const fn new(inner: T) -> Self {
Self { inner }
}
#[inline]
pub fn into_inner(self) -> T {
self.inner
}
pub const NAME: &'static str = NAME;
pub const fn name(&self) -> &'static str {
Self::NAME
}
}
impl<T, const NAME: &'static str> From<T> for Field<T, NAME> {
#[inline]
fn from(inner: T) -> Self {
Self::new(inner)
}
}
impl<T, const NAME: &'static str> Deref for Field<T, NAME> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T, const NAME: &'static str> DerefMut for Field<T, NAME> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: FromStr, const NAME: &'static str> FromStr for Field<T, NAME> {
type Err = T::Err;
#[inline]
fn from_str(input: &str) -> Result<Self, Self::Err> {
Ok(Self::new(input.parse()?))
}
}
impl<T: fmt::Debug, const NAME: &'static str> fmt::Debug for Field<T, NAME> {
#[inline]
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(w, "{} = {:?}", NAME, self.inner)
}
}
impl<T: fmt::Display, const NAME: &'static str> fmt::Display for Field<T, NAME> {
#[inline]
fn fmt(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(w)
}
}
impl<T: Reply, const NAME: &'static str> Reply for Field<T, NAME> {
#[inline]
fn into_response(self) -> Response {
self.inner.into_response()
}
}
impl<T: Serialize, const NAME: &'static str> Serialize for Field<T, NAME> {
fn serialize<S: ser::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
use ser::SerializeStruct;
let mut s = s.serialize_struct("Field", 1)?;
s.serialize_field(Self::NAME, &self.inner)?;
s.end()
}
}
trait Named {
const NAME: &'static str;
}
impl<T, const NAME: &'static str> Named for Field<T, NAME> {
const NAME: &'static str = NAME;
}
impl<'de, T: Deserialize<'de>, const NAME: &'static str> Deserialize<'de> for Field<T, NAME> {
fn deserialize<D: de::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
enum Tag<Up> {
Good(PhantomData<fn(Up)>),
Ignore(PhantomData<fn(Up)>),
}
struct TagVisitor<Up>(PhantomData<fn(Up)>);
impl<'de, Up: Named> de::Visitor<'de> for TagVisitor<Up> {
type Value = Tag<Up>;
#[inline]
fn expecting(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(w, "field identifier")
}
#[inline]
fn visit_u64<E: de::Error>(self, val: u64) -> Result<Self::Value, E> {
if val == 0 {
return Ok(Tag::Good(PhantomData));
}
Err(de::Error::invalid_value(
de::Unexpected::Unsigned(val),
&"field index 0 <= i < 1",
))
}
#[inline]
fn visit_str<E: de::Error>(self, val: &str) -> Result<Self::Value, E> {
if val == Up::NAME {
Ok(Tag::Good(PhantomData))
} else {
Ok(Tag::Ignore(PhantomData))
}
}
#[inline]
fn visit_bytes<E: de::Error>(self, val: &[u8]) -> Result<Self::Value, E> {
if val == Up::NAME.as_bytes() {
Ok(Tag::Good(PhantomData))
} else {
Ok(Tag::Ignore(PhantomData))
}
}
}
impl<'de, Up: Named> Deserialize<'de> for Tag<Up> {
#[inline]
fn deserialize<D: de::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
d.deserialize_identifier(TagVisitor::<Up>(PhantomData))
}
}
struct Visitor<T, const NAME: &'static str>(PhantomData<fn(Field<T, NAME>)>);
impl<'de, T: Deserialize<'de>, const NAME: &'static str> de::Visitor<'de> for Visitor<T, NAME> {
type Value = Field<T, NAME>;
#[inline]
fn expecting(&self, w: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(w, "struct Field")
}
#[inline]
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let inner = seq
.next_element::<T>()?
.ok_or_else(|| de::Error::invalid_length(0, &"struct Field with 1 element"))?;
Ok(Field { inner })
}
#[inline]
fn visit_map<A: de::MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let mut inner: Option<T> = None;
while let Some(tag) = map.next_key::<Tag<Self::Value>>()? {
match tag {
Tag::Good(_) if inner.is_some() => {
return Err(de::Error::duplicate_field(NAME))
}
Tag::Good(_) => {
inner = Some(map.next_value()?);
}
Tag::Ignore(_) => {
map.next_value::<de::IgnoredAny>()?;
}
}
}
let inner = inner.ok_or_else(|| de::Error::missing_field(NAME))?;
Ok(Field { inner })
}
}
d.deserialize_struct("Field", &[NAME], Visitor::<T, NAME>(PhantomData))
}
}
#[derive(Copy, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
#[doc(hidden)]
pub struct RNil {
#[serde(skip)]
__: (),
}
#[derive(Copy, Clone, Serialize, Deserialize)]
#[doc(hidden)]
pub struct RCons<T, Tail> {
#[serde(flatten)]
head: T,
#[serde(flatten)]
tail: Tail,
}
pub trait IsoEncode<'a> {
type Repr: Serialize;
fn as_repr(&'a self) -> Self::Repr;
}
impl<'a> IsoEncode<'a> for HNil {
type Repr = RNil;
#[inline]
fn as_repr(&'a self) -> Self::Repr {
RNil { __: () }
}
}
impl<'a, T: Serialize + 'a, Tail: IsoEncode<'a>> IsoEncode<'a> for HCons<T, Tail> {
type Repr = RCons<&'a T, Tail::Repr>;
#[inline]
fn as_repr(&'a self) -> Self::Repr {
RCons {
head: &self.head,
tail: self.tail.as_repr(),
}
}
}
pub trait IsoDecode {
type Repr: DeserializeOwned;
fn from_repr(repr: Self::Repr) -> Self;
}
impl IsoDecode for HNil {
type Repr = RNil;
#[inline]
fn from_repr(_: Self::Repr) -> Self {
HNil
}
}
impl<T: DeserializeOwned, Tail: IsoDecode> IsoDecode for HCons<T, Tail> {
type Repr = RCons<T, Tail::Repr>;
#[inline]
fn from_repr(repr: Self::Repr) -> Self {
HCons {
head: repr.head,
tail: Tail::from_repr(repr.tail),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck_macros::quickcheck;
fn iso_de<T: IsoDecode>(input: &str) -> T {
T::from_repr(serde_json::from_str(input).unwrap())
}
fn iso_ser<'a, T: IsoEncode<'a>>(input: &'a T) -> String {
serde_json::to_string(&input.as_repr()).unwrap()
}
fn ser_de_ser<T: IsoDecode + PartialEq + fmt::Debug>(input: T)
where T: for<'a> IsoEncode<'a> {
let encoded = iso_ser(&input);
let decoded: T = iso_de(&encoded);
assert_eq!(input, decoded);
}
fn de_ser_de<T: IsoDecode>(input: &str)
where T: for<'a> IsoEncode<'a> {
let decoded: T = iso_de(input);
let encoded = iso_ser(&decoded);
assert_eq!(input, encoded);
}
#[quickcheck]
fn serde_iso_roundtrips(x: u32, y: String, z: f32) {
ser_de_ser(r![x = x, y = y.clone()]);
ser_de_ser(r![y = y, x = x, z = z]);
de_ser_de::<R![x: u32, y: String, z: f32]>(r#"{"x":32,"y":"neat","z":4.4}"#);
}
}