use std::marker::PhantomData;
use std::sync::Arc;
pub trait PartialLens: Clone {
type From;
type To;
fn try_get<R>(&self, s: R) -> Option<Arc<Self::To>>
where
R: AsRef<Self::From>;
fn try_put<Convert, R>(&self, v: Option<Convert>, s: R) -> Option<Self::From>
where
R: AsRef<Self::From>,
Arc<Self::To>: From<Convert>;
fn try_chain<L, Next>(&self, next: &L) -> Compose<Self::From, Self::To, Next, Self, L>
where
L: PartialLens<From = Self::To, To = Next>,
{
compose(self, next)
}
}
pub trait Lens: PartialLens {
fn get<R>(&self, s: R) -> Arc<Self::To>
where
R: AsRef<Self::From>,
{
self.try_get(s).unwrap()
}
fn put<Convert, R>(&self, v: Convert, s: R) -> Self::From
where
R: AsRef<Self::From>,
Arc<Self::To>: From<Convert>,
{
self.try_put(Some(v), s).unwrap()
}
fn chain<L, Next>(&self, next: &L) -> Compose<Self::From, Self::To, Next, Self, L>
where
L: Lens<From = Self::To, To = Next>,
{
compose(self, next)
}
}
pub struct Compose<A, B, C, L, R>
where
L: PartialLens<From = A, To = B>,
R: PartialLens<From = B, To = C>,
{
left: Arc<L>,
right: Arc<R>,
phantom_a: PhantomData<A>,
phantom_b: PhantomData<B>,
phantom_c: PhantomData<C>,
}
impl<A, B, C, L, R> Clone for Compose<A, B, C, L, R>
where
L: PartialLens<From = A, To = B>,
R: PartialLens<From = B, To = C>,
{
fn clone(&self) -> Self {
Compose {
left: self.left.clone(),
right: self.right.clone(),
phantom_a: PhantomData,
phantom_b: PhantomData,
phantom_c: PhantomData,
}
}
}
impl<A, B, C, L, R> PartialLens for Compose<A, B, C, L, R>
where
L: PartialLens<From = A, To = B>,
R: PartialLens<From = B, To = C>,
{
type From = A;
type To = C;
fn try_get<Re>(&self, s: Re) -> Option<Arc<C>>
where
Re: AsRef<A>,
{
match self.left.try_get(s) {
None => None,
Some(s2) => self.right.try_get(s2),
}
}
fn try_put<FromC, Re>(&self, v: Option<FromC>, s: Re) -> Option<A>
where
Re: AsRef<A>,
Arc<C>: From<FromC>,
{
self.left
.try_get(&s)
.and_then(|s2| self.right.try_put(v, s2))
.and_then(|s3| self.left.try_put(Some(s3), &s))
}
}
impl<A, B, C, L, R> Lens for Compose<A, B, C, L, R>
where
L: Lens<From = A, To = B>,
R: Lens<From = B, To = C>,
{
}
pub fn compose<A, B, C, L, R>(left: &L, right: &R) -> Compose<A, B, C, L, R>
where
L: PartialLens<From = A, To = B>,
R: PartialLens<From = B, To = C>,
{
Compose {
left: Arc::new(left.clone()),
right: Arc::new(right.clone()),
phantom_a: PhantomData,
phantom_b: PhantomData,
phantom_c: PhantomData,
}
}