Algebraic Effects System for Rust
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

test lens macro

+127 -18
+8 -7
Cargo.lock
··· 9 9 checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" 10 10 11 11 [[package]] 12 - name = "fx-rs" 12 + name = "fx" 13 13 version = "0.0.0-git" 14 14 dependencies = [ 15 15 "dyn-clone", 16 16 ] 17 17 18 18 [[package]] 19 - name = "integration" 19 + name = "fx-lens" 20 20 version = "0.0.0-git" 21 21 dependencies = [ 22 - "fx-rs", 22 + "proc-macro2", 23 + "quote", 24 + "syn", 23 25 ] 24 26 25 27 [[package]] 26 - name = "lens_macros" 28 + name = "integration" 27 29 version = "0.0.0-git" 28 30 dependencies = [ 29 - "proc-macro2", 30 - "quote", 31 - "syn", 31 + "fx", 32 + "fx-lens", 32 33 ] 33 34 34 35 [[package]]
+2
Cargo.toml
··· 1 1 [workspace] 2 + resolver = "3" 3 + 2 4 members = [ 3 5 "fx", 4 6 "integration",
+1 -1
fx/Cargo.toml
··· 1 1 [package] 2 - name = "fx-rs" 2 + name = "fx" 3 3 description = "Algebraic Effects inspired by Kyo and Fx.go" 4 4 readme = "README.md" 5 5 keywords.workspace = true
+2 -1
integration/Cargo.toml
··· 9 9 edition.workspace = true 10 10 11 11 [dependencies] 12 - fx-rs = { path = "../fx" } 12 + fx = { path = "../fx" } 13 + fx-lens = { path = "../lens_macros" }
integration/src/lib.rs

This is a binary file and will not be displayed.

-4
integration/src/main.rs
··· 1 - fn main() { 2 - // Integration tests or binaries can be placed here. 3 - println!("Integration crate ready."); 4 - }
+78
integration/tests/lens_macro_test.rs
··· 1 + use fx::{Fx, State}; 2 + use fx_lens::Lenses; 3 + 4 + #[derive(Clone, Debug, PartialEq, Lenses)] 5 + struct ST { 6 + a: i32, 7 + b: String, 8 + } 9 + 10 + #[test] 11 + fn integration_focus_out() { 12 + let e: Fx<String, ()> = State::set("hello".to_string()).then(Fx::value(())); 13 + let e: Fx<ST, ()> = e.via(ST::lens_b().zoom_out()); 14 + let e = e.then(State::get()); 15 + let result = e 16 + .provide(ST { 17 + a: 0, 18 + b: "world".to_string(), 19 + }) 20 + .eval(); 21 + assert_eq!( 22 + result, 23 + ST { 24 + a: 0, 25 + b: "hello".to_string() 26 + } 27 + ); 28 + } 29 + 30 + #[test] 31 + fn integration_focus_in() { 32 + let e: Fx<ST, ()> = Fx::immediate( 33 + ST { 34 + a: 42, 35 + b: "hello".to_string(), 36 + }, 37 + (), 38 + ); 39 + let e: Fx<ST, ()> = e.via(ST::lens_b().zoom_in(|()| State::set("bye".to_string()).map(|_| ()))); 40 + let e = e.then(State::get()); 41 + let result = e 42 + .provide(ST { 43 + a: 0, 44 + b: "bad".to_string(), 45 + }) 46 + .eval(); 47 + assert_eq!( 48 + result, 49 + ST { 50 + a: 42, 51 + b: "bye".to_string() 52 + } 53 + ); 54 + } 55 + 56 + #[test] 57 + fn integration_focus_in_and_out() { 58 + let inner: Fx<i32, ()> = Fx::immediate(10, ()); 59 + let outer: Fx<ST, ()> = inner 60 + .via(ST::lens_a().zoom_out()) 61 + .then(State::map(|s: ST| ST { a: s.a * 2, ..s })) 62 + .then(Fx::value(())); 63 + let back: Fx<ST, i32> = outer.via(ST::lens_a().zoom_in(|_| State::<i32>::map(|n| n + 10))); 64 + let e = back.then(State::get()); 65 + let result = e 66 + .provide(ST { 67 + a: 0, 68 + b: "hello".to_owned(), 69 + }) 70 + .eval(); 71 + assert_eq!( 72 + result, 73 + ST { 74 + a: 30, 75 + b: "hello".to_owned() 76 + } 77 + ); 78 + }
+1 -1
lens_macros/Cargo.toml
··· 1 1 [package] 2 - name = "lens_macros" 2 + name = "fx-lens" 3 3 keywords.workspace = true 4 4 license.workspace = true 5 5 authors.workspace = true
+35 -4
lens_macros/src/lib.rs
··· 1 1 use proc_macro::TokenStream; 2 + use quote::{format_ident, quote}; 3 + use syn::{Data, DeriveInput, Fields, parse_macro_input}; 2 4 3 - #[proc_macro_derive(Lens)] 4 - pub fn derive_lens(_input: TokenStream) -> TokenStream { 5 - // Implementation will go here 6 - TokenStream::new() 5 + #[proc_macro_derive(Lenses)] 6 + pub fn derive_lens(input: TokenStream) -> TokenStream { 7 + let input = parse_macro_input!(input as DeriveInput); 8 + let name = &input.ident; 9 + let vis = &input.vis; 10 + let generics = &input.generics; 11 + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 12 + 13 + let fields = match &input.data { 14 + Data::Struct(data) => match &data.fields { 15 + Fields::Named(fields) => &fields.named, 16 + _ => panic!("Lens can only be derived for structs with named fields"), 17 + }, 18 + _ => panic!("Lens can only be derived for structs"), 19 + }; 20 + 21 + let lenses = fields.iter().map(|f| { 22 + let fname = f.ident.as_ref().unwrap(); 23 + let fty = &f.ty; 24 + let lens_name = format_ident!("lens_{}", fname); 25 + quote! { 26 + #vis fn #lens_name<'f>() -> ::fx::Lens<'f, #name #ty_generics, #fty> { 27 + ::fx::Lens::new(|s: #name #ty_generics| s.#fname.clone(), |mut s, v| { s.#fname = v; s }) 28 + } 29 + } 30 + }); 31 + 32 + let expanded = quote! { 33 + impl #impl_generics #name #ty_generics #where_clause { 34 + #(#lenses)* 35 + } 36 + }; 37 + TokenStream::from(expanded) 7 38 }