use std::marker::PhantomData;
use std::sync::Arc;
use shared::Shared;
pub trait PartialLens: Clone {
type From;
type To;
fn try_get(&self, s: &Self::From) -> Option<Arc<Self::To>>;
fn try_put<Convert>(&self, v: Option<Convert>, s: &Self::From) -> Option<Self::From>
where
Convert: Shared<Self::To>;
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(&self, s: &Self::From) -> Arc<Self::To> {
self.try_get(s).unwrap()
}
fn put<Convert>(&self, v: Convert, s: &Self::From) -> Self::From
where
Convert: Shared<Self::To>,
{
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(&self, s: &A) -> Option<Arc<C>> {
match self.left.try_get(s) {
None => None,
Some(s2) => self.right.try_get(&s2),
}
}
fn try_put<FromC>(&self, v: Option<FromC>, s: &A) -> Option<A>
where
FromC: Shared<C>,
{
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,
}
}
pub struct GeneralLens<From, To> {
get: Arc<Fn(&From) -> Arc<To>>,
put: Arc<Fn(&From, Arc<To>) -> From>,
}
impl<From, To> GeneralLens<From, To> {
pub fn new(
get: Arc<Fn(&From) -> Arc<To>>,
put: Arc<Fn(&From, Arc<To>) -> From>,
) -> GeneralLens<From, To> {
GeneralLens { get, put }
}
}
impl<From, To> Clone for GeneralLens<From, To> {
fn clone(&self) -> Self {
GeneralLens {
get: self.get.clone(),
put: self.put.clone(),
}
}
}
impl<A, B> PartialLens for GeneralLens<A, B> {
type From = A;
type To = B;
fn try_get(&self, s: &A) -> Option<Arc<B>> {
Some((self.get)(s))
}
fn try_put<Convert>(&self, v: Option<Convert>, s: &A) -> Option<A>
where
Convert: Shared<B>,
{
Some((self.put)(s, v.unwrap().shared()))
}
}
impl<A, B> Lens for GeneralLens<A, B> {
fn get(&self, s: &A) -> Arc<B> {
(self.get)(s)
}
fn put<Convert>(&self, v: Convert, s: &A) -> A
where
Convert: Shared<B>,
{
(self.put)(s, v.shared())
}
}
#[macro_export]
macro_rules! lens {
( $from:ident : $headpath:ident : $headto:ident : $($tail:tt):* ) => {
$crate::lens::compose(&lens!($from : $headpath : $headto), &lens!($headto : $($tail):*))
};
( $from:ident : $path:ident : $to:ident ) => {
$crate::lens::GeneralLens::<$from, $to>::new(
::std::sync::Arc::new(|st| st.$path.clone()),
::std::sync::Arc::new(|st, v| {
$from {
$path: v,
..st.clone()
}
})
)
};
}
#[cfg(test)]
mod test {
use super::*;
#[derive(Clone)]
struct Inner {
omg: Arc<String>,
wtf: Arc<String>,
}
#[derive(Clone)]
struct Outer {
inner: Arc<Inner>,
}
#[test]
fn struct_lens() {
let l = lens!(Outer: inner: Inner: omg: String);
let omglol = "omg lol".to_string();
let inner = Inner {
omg: Arc::new(omglol.clone()),
wtf: Arc::new("nope".to_string()),
};
let outer = Outer {
inner: Arc::new(inner),
};
assert_eq!(Arc::new(omglol), l.get(&outer))
}
}