From e62f421fa6f418fbd9669a8ad099613d2928d2ce Mon Sep 17 00:00:00 2001 From: annieversary Date: Fri, 5 Nov 2021 10:12:03 +0000 Subject: [PATCH] init --- .gitignore | 2 ++ Cargo.toml | 8 +++++ src/lenses/first_tuple.rs | 47 +++++++++++++++++++++++++++ src/lenses/mod.rs | 2 ++ src/lib.rs | 68 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lenses/first_tuple.rs create mode 100644 src/lenses/mod.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e5f1841 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "bad-optics" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/src/lenses/first_tuple.rs b/src/lenses/first_tuple.rs new file mode 100644 index 0000000..d7b2e30 --- /dev/null +++ b/src/lenses/first_tuple.rs @@ -0,0 +1,47 @@ +use crate::{LensOver, LensView}; + +pub struct _0; + +macro_rules! make { + ($f:ident, ( $( $v:ident ),* ), ( $( $t:ident ),* ) ) => { + impl< $($t,)* > LensView<( $($t,)* )> for _0 { + type Field = T; + + fn view(( $($v,)* ): ($($t,)*)) -> Self::Field { + $f + } + } + + impl< $($t,)* > LensOver<( $($t,)* )> for _0 { + fn over( + mut tup: ($($t,)*), + f: &dyn Fn(Self::Field) -> Self::Field + ) -> ( $($t,)* ) { + tup.0 = f(tup.0); + tup + } + } + impl<'a, $($t,)* > LensView<&'a ( $($t,)* )> for _0 { + type Field = &'a T; + + fn view(( $($v,)* ): &'a ($($t,)*)) -> Self::Field { + $f + } + } + impl<'a, $($t,)* > LensView<&'a mut ( $($t,)* )> for _0 { + type Field = &'a mut T; + + fn view(( $($v,)* ): &'a mut ($($t,)*)) -> Self::Field { + $f + } + } + }; +} + +make!(t, (t, _u), (T, U)); +make!(t, (t, _u, _v), (T, U, V)); +make!(t, (t, _u, _v, _w), (T, U, V, W)); +make!(t, (t, _u, _v, _w, _x), (T, U, V, W, X)); +make!(t, (t, _u, _v, _w, _x, _y), (T, U, V, W, X, Y)); +make!(t, (t, _u, _v, _w, _x, _y, _z), (T, U, V, W, X, Y, Z)); +// not doing more cause i'm lazy, open a pr if you need more :) diff --git a/src/lenses/mod.rs b/src/lenses/mod.rs new file mode 100644 index 0000000..42052fd --- /dev/null +++ b/src/lenses/mod.rs @@ -0,0 +1,2 @@ +mod first_tuple; +pub use first_tuple::_0; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d382d15 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,68 @@ +pub trait LensView { + type Field; + + fn view(thing: T) -> Self::Field; +} + +pub trait LensOver: LensView { + fn over(thing: T, f: &dyn Fn(Self::Field) -> Self::Field) -> T; +} + +pub fn view>(_lens: L, thing: T) -> L::Field { + L::view(thing) +} +pub fn over>(_lens: L, thing: T, f: &dyn Fn(L::Field) -> L::Field) -> T { + L::over(thing, f) +} + +// TODO add Fn implementation for lenses +// with one param it should be view, with two it should be over +// TODO add std::ops::Add to combine lenses + +pub mod lenses; + +#[cfg(test)] +mod tests { + use super::{lenses::_0, *}; + + #[test] + fn view_first_from_tuple() { + let a = (1, 2); + assert_eq!(view(_0, a), 1); + + // you can call it both ways + let a = (1, 2); + assert_eq!(_0::view(a), 1); + + let a = (1, 2); + assert_eq!(view(_0, &a), &1); + + let mut a = (1, 2); + assert_eq!(view(_0, &mut a), &mut 1); + + let a = (1, 2, 3); + assert_eq!(view(_0, a), 1); + + let a = (1, 2, 3); + assert_eq!(view(_0, &a), &1); + } + + #[test] + fn view_first_from_tuple_mut_works() { + let mut a = (1, 2); + *view(_0, &mut a) += 1; + + assert_eq!(a, (2, 2)); + } + + #[test] + fn over_first_from_tuple_mut_works() { + let a = (1, 2); + let a = over(_0, a, &|v| v + 1); + assert_eq!(a, (2, 2)); + + let a = (1, 2); + let a = _0::over(a, &|v| v + 1); + assert_eq!(a, (2, 2)); + } +}