use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::sync::Arc;
#[derive(
Serialize, Deserialize, JsonSchema, Debug, Clone, Hash, PartialEq, PartialOrd, Ord, Eq,
)]
#[serde(try_from = "Y")]
#[serde(into = "Y")]
#[serde(
bound = "T: Clone, Y: Serialize + for<'d> Deserialize<'d> + JsonSchema + std::fmt::Debug + Clone, Y: Serialize + From<Self>, Self: TryFrom<Y>, <Self as TryFrom<Y>>::Error : std::fmt::Display"
)]
pub struct ReversePath<T, Y = String> {
past: Option<Arc<ReversePath<T, Y>>>,
this: T,
_pd: PhantomData<Y>,
}
pub struct RPI<'a, T, Y> {
inner: Option<&'a ReversePath<T, Y>>,
}
impl<'a, T, Y> Iterator for RPI<'a, T, Y> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let ret = self.inner.map(|x| &x.this);
match self.inner.map(|x| x.past.as_ref()) {
Some(Some(x)) => {
self.inner = Some(x);
}
_ => {
self.inner = None;
}
}
ret
}
}
use std::convert::TryFrom;
impl<T, Y> TryFrom<Vec<T>> for ReversePath<T, Y> {
type Error = &'static str;
fn try_from(v: Vec<T>) -> Result<Self, Self::Error> {
match v
.into_iter()
.fold(None, |x, v| Some(Self::push(x, v)))
.map(Arc::try_unwrap)
{
Some(Ok(r)) => Ok(r),
_ => Err("Reverse Path must have at least one element."),
}
}
}
impl<T: Clone, Y> From<ReversePath<T, Y>> for Vec<T> {
fn from(r: ReversePath<T, Y>) -> Self {
let mut v: Vec<T> = r.iter().cloned().collect();
v.reverse();
v
}
}
impl<T: Clone, Y> From<T> for ReversePath<T, Y> {
fn from(this: T) -> Self {
ReversePath {
past: None,
this,
_pd: Default::default(),
}
}
}
pub struct MkReversePath<T, Y>(Option<Arc<ReversePath<T, Y>>>);
impl<T, Y> MkReversePath<T, Y> {
pub fn unwrap(self) -> Arc<ReversePath<T, Y>> {
if let Some(x) = self.0 {
x
} else {
panic!("Vector must have at least one root path")
}
}
}
impl<T, Y> From<Vec<T>> for MkReversePath<T, Y> {
fn from(v: Vec<T>) -> Self {
let mut rp: Option<Arc<ReversePath<T, Y>>> = None;
for val in v {
let new: Arc<ReversePath<T, Y>> = ReversePath::push(rp, val);
rp = Some(new);
}
MkReversePath(rp)
}
}
impl<T, Y> ReversePath<T, Y> {
pub fn push(v: Option<Arc<ReversePath<T, Y>>>, s: T) -> Arc<ReversePath<T, Y>> {
Arc::new(Self::push_owned(v, s))
}
pub fn push_owned(v: Option<Arc<ReversePath<T, Y>>>, s: T) -> ReversePath<T, Y> {
ReversePath::<T, Y> {
past: v,
this: s,
_pd: Default::default(),
}
}
pub fn iter(&self) -> RPI<'_, T, Y> {
RPI { inner: Some(self) }
}
}
#[cfg(test)]
mod test {
use super::*;
use std::convert::TryInto;
#[test]
fn test_reverse_path_into_vec() {
assert_eq!(
Vec::<i64>::from(
ReversePath::<i64, Vec<i64>>::push(Some(ReversePath::push(None, 1i64)), 5i64,)
.as_ref()
.clone()
),
vec![1i64, 5]
);
}
#[test]
fn test_reverse_path_from_vec() {
assert_eq!(
ReversePath::<i64, Vec<i64>>::push(Some(ReversePath::push(None, 1i64)), 5,)
.as_ref()
.clone(),
vec![1i64, 5].try_into().unwrap()
);
}
#[test]
fn test_reverse_path_into_serde() -> Result<(), Box<dyn std::error::Error>> {
assert_eq!(
serde_json::to_string(
ReversePath::<i64, Vec<i64>>::push(Some(ReversePath::push(None, 1i64)), 5,)
.as_ref()
)?,
"[1,5]"
);
Ok(())
}
#[test]
fn test_reverse_path_from_serde() -> Result<(), Box<dyn std::error::Error>> {
let v: ReversePath<i64, Vec<i64>> = serde_json::from_str("[1,5]")?;
assert_eq!(
ReversePath::push(Some(ReversePath::push(None, 1i64)), 5,).as_ref(),
&v
);
Ok(())
}
#[test]
fn test_eq() {
assert_eq!(
ReversePath::<i64, Vec<i64>>::push(Some(ReversePath::push(None, 1i64)), 5,),
ReversePath::push(Some(ReversePath::push(None, 1i64)), 5,)
);
let a = (0..100)
.fold(None, |x, y| Some(ReversePath::<i64, Vec<i64>>::push(x, y)))
.unwrap();
assert_eq!(a, a.clone());
let b = (0..100)
.fold(None, |x, y| Some(ReversePath::push(x, y)))
.unwrap();
assert_eq!(a, b);
}
#[test]
fn test_neq() {
assert_ne!(
ReversePath::<i64, Vec<i64>>::push(Some(ReversePath::push(None, 1i64)), 5,),
ReversePath::push(Some(ReversePath::push(None, 0i64)), 5,)
);
let a = (0..100)
.fold(None, |x, y| Some(ReversePath::<i64, Vec<i64>>::push(x, y)))
.unwrap();
let b = (0..101)
.fold(None, |x, y| Some(ReversePath::<i64, Vec<i64>>::push(x, y)))
.unwrap();
assert_ne!(a, b);
}
}