├── .gitignore ├── Cargo.toml ├── README.md ├── functor_derive ├── Cargo.toml ├── README.md ├── examples │ └── readme.rs ├── src │ ├── impl_alloc.rs │ ├── impl_core.rs │ ├── impl_std.rs │ └── lib.rs └── tests │ ├── attributes.rs │ ├── bounds.rs │ ├── funcmap.rs │ ├── funcmap │ ├── decorations.rs │ ├── fallible.rs │ ├── hygiene.rs │ ├── impl_alloc.rs │ ├── impl_core.rs │ ├── impl_std.rs │ ├── lints.rs │ ├── mod.rs │ ├── multi_param.rs │ ├── opts_crate.rs │ ├── opts_params.rs │ ├── single_param.rs │ └── variants.rs │ ├── recursion.rs │ ├── simple.rs │ ├── std.rs │ └── try.rs └── functor_derive_lib ├── Cargo.toml ├── README.md └── src ├── generate_fmap_body.rs ├── generate_map.rs ├── lib.rs ├── map.rs └── parse_attribute.rs /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | Cargo.lock 3 | .idea -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "functor_derive_lib", 6 | "functor_derive", 7 | ] 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | functor_derive/README.md -------------------------------------------------------------------------------- /functor_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "functor_derive" 3 | version = "0.4.3" 4 | authors = ["Jonathan Brouwer ", "Julia Dijkstra"] 5 | description = "A derive macro to derive a functor for a type." 6 | keywords = ["proc-macro", "derive", "functor"] 7 | edition = "2021" 8 | license = "MIT" 9 | repository = "https://github.com/binary-banter/functor_derive" 10 | 11 | [dependencies] 12 | functor_derive_lib = { version = "=0.4.3"} 13 | paste = "1.0.14" 14 | -------------------------------------------------------------------------------- /functor_derive/README.md: -------------------------------------------------------------------------------- 1 | # Functor Derive 2 | 3 | [![github](https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github)](https://github.com/binary-banter/functor_derive) 4 |  [![crates-io](https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust)](https://crates.io/crates/functor_derive) 5 |  [![docs-rs](https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs)](https://docs.rs/functor_derive/) 6 | 7 | This crate can generate a functor for generic structs and enums. 8 | 9 | A functor is a trait that contains an `fmap` function that maps a generic parameter. 10 | This enables you to transform the contents of any type without altering its shape. 11 | 12 | The following example demonstrates how to derive a functor, providing you with an `fmap` method. 13 | For more intricate examples, refer to the tests directory in 14 | the [project repository](https://github.com/binary-banter/functor_derive/tree/main/functor_derive/tests). 15 | 16 | ```rust 17 | use functor_derive::Functor; 18 | 19 | #[derive(Functor)] 20 | struct MyType { 21 | value: T, 22 | list: Vec, 23 | unaffected: bool, 24 | } 25 | 26 | fn main() { 27 | let original = MyType { value: 42, list: vec![1, 3], unaffected: false }; 28 | let transformed = original.fmap(|x| (x, x * 2)); 29 | 30 | assert_eq!(transformed.value, (42, 84)); 31 | assert_eq!(transformed.list, vec![(1, 2), (3, 6)]); 32 | } 33 | ``` 34 | 35 | Additionally, a `try_fmap` function is generated. This can be useful for fallible transformations. 36 | 37 | ```rust 38 | let original = MyType { value: "42", list: vec!["1", "3"], unaffected: false }; 39 | let transformed = original.try_fmap(|x| x.parse::())?; 40 | ``` 41 | 42 | ## Attribute 43 | 44 | You can invoke the derive macro in multiple ways. Omitting the attribute defaults to deriving the `Functor` trait for 45 | the first generic type parameter, as illustrated in the first example above. 46 | 47 | Alternatively, you can specify a default type to override the derive macro, which will prevent the derive macro choosing 48 | the first 49 | generic type parameter. This is done as follows: 50 | 51 | ```rust 52 | #[derive(Functor)] 53 | #[functor(T2)] 54 | struct MyType { 55 | field_1: T1, 56 | field_2: T2, 57 | } 58 | ``` 59 | 60 | Sometimes, you might want to rename your `fmap` function using the as keyword. The following example generates the 61 | method `fmap_keys`. 62 | 63 | ```rust 64 | #[derive(Functor)] 65 | #[functor(K as keys)] 66 | struct MyType { 67 | keys: Vec 68 | } 69 | ``` 70 | 71 | The above options can be combined to generate multiple implementations, by separating the options with commas. 72 | The code below generates 3 methods: `fmap`, `fmap_keys` and `fmap_values`. 73 | 74 | ```rust 75 | use std::collections::HashMap; 76 | use std::hash::Hash; 77 | 78 | #[functor(V, K as keys, V as values)] 79 | struct MyHashMap { 80 | v: HashMap 81 | } 82 | ``` 83 | 84 | ## Supported features 85 | 86 | This crate can handle the following perfectly: 87 | 88 | - Structs - except for unit structs, which cannot be generic 89 | - Enums 90 | - Arrays 91 | - Tuples 92 | - `std::collections`: Vec, VecDeque, LinkedList, HashSet, HashMap, BTreeMap, Result, Option, PhantomData 93 | - Nested types, like `Option>` 94 | - (Mutually) recursive types 95 | - Bounded parameters, like `T: Display` 96 | 97 | If you find a case where the derive macro fails, feel free to open an 98 | issue [here](https://github.com/binary-banter/functor_derive/issues) 99 | -------------------------------------------------------------------------------- /functor_derive/examples/readme.rs: -------------------------------------------------------------------------------- 1 | use functor_derive::Functor; 2 | 3 | #[derive(Functor)] 4 | struct MyType { 5 | value: T, 6 | list: Vec, 7 | unaffected: bool, 8 | } 9 | 10 | fn main() { 11 | let original = MyType { 12 | value: 42, 13 | list: vec![1, 3], 14 | unaffected: false, 15 | }; 16 | let transformed = original.fmap(|x| (x, x * 2)); 17 | 18 | assert_eq!(transformed.value, (42, 84)); 19 | assert_eq!(transformed.list, vec![(1, 2), (3, 6)]); 20 | } 21 | -------------------------------------------------------------------------------- /functor_derive/src/impl_alloc.rs: -------------------------------------------------------------------------------- 1 | use crate::{Functor, Functor0, Functor1, FunctorOrdKeys, FunctorValues}; 2 | use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}; 3 | 4 | impl Functor for Vec { 5 | type Target = Vec; 6 | 7 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 8 | self.__fmap_0_ref(&f) 9 | } 10 | 11 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 12 | self.__try_fmap_0_ref(&f) 13 | } 14 | } 15 | 16 | impl Functor0 for Vec { 17 | type Target = Vec; 18 | 19 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 20 | self.into_iter().map(f).collect() 21 | } 22 | 23 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 24 | self.into_iter().map(f).collect() 25 | } 26 | } 27 | 28 | impl Functor for Box { 29 | type Target = Box; 30 | 31 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 32 | self.__fmap_0_ref(&f) 33 | } 34 | 35 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 36 | self.__try_fmap_0_ref(&f) 37 | } 38 | } 39 | 40 | impl Functor0 for Box { 41 | type Target = Box; 42 | 43 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 44 | Box::new(f(*self)) 45 | } 46 | 47 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 48 | f(*self).map(Box::new) 49 | } 50 | } 51 | 52 | impl Functor for VecDeque { 53 | type Target = VecDeque; 54 | 55 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 56 | self.__fmap_0_ref(&f) 57 | } 58 | 59 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 60 | self.__try_fmap_0_ref(&f) 61 | } 62 | } 63 | 64 | impl Functor0 for VecDeque { 65 | type Target = VecDeque; 66 | 67 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 68 | self.into_iter().map(f).collect() 69 | } 70 | 71 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 72 | self.into_iter().map(f).collect() 73 | } 74 | } 75 | 76 | impl Functor for LinkedList { 77 | type Target = LinkedList; 78 | 79 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 80 | self.__fmap_0_ref(&f) 81 | } 82 | 83 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 84 | self.__try_fmap_0_ref(&f) 85 | } 86 | } 87 | 88 | impl Functor0 for LinkedList { 89 | type Target = LinkedList; 90 | 91 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 92 | self.into_iter().map(f).collect() 93 | } 94 | 95 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 96 | self.into_iter().map(f).collect() 97 | } 98 | } 99 | 100 | impl Functor for BTreeMap { 101 | type Target = BTreeMap; 102 | 103 | /// By default BTreeMaps map their Value generic. 104 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 105 | self.__fmap_1_ref(&f) 106 | } 107 | 108 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 109 | self.__try_fmap_1_ref(&f) 110 | } 111 | } 112 | 113 | impl FunctorOrdKeys for BTreeMap { 114 | type Target = BTreeMap; 115 | 116 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 117 | self.into_iter().map(|(k, v)| (f(k), v)).collect() 118 | } 119 | 120 | fn __try_fmap_0_ref( 121 | self, 122 | f: &impl Fn(A) -> Result, 123 | ) -> Result, E> { 124 | self.into_iter() 125 | .map(|(k, v)| f(k).map(|k| (k, v))) 126 | .collect() 127 | } 128 | } 129 | 130 | impl FunctorValues for BTreeMap { 131 | type Target = BTreeMap; 132 | 133 | fn fmap_values(self, f: impl Fn(A) -> B) -> Self::Target { 134 | self.__fmap_1_ref(&f) 135 | } 136 | 137 | fn try_fmap_values(self, f: impl Fn(A) -> Result) -> Result, E> { 138 | self.__try_fmap_1_ref(&f) 139 | } 140 | } 141 | 142 | impl Functor1 for BTreeMap { 143 | type Target = BTreeMap; 144 | 145 | fn __fmap_1_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 146 | self.into_iter().map(|(k, v)| (k, f(v))).collect() 147 | } 148 | 149 | fn __try_fmap_1_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 150 | self.into_iter() 151 | .map(|(k, v)| f(v).map(|v| (k, v))) 152 | .collect() 153 | } 154 | } 155 | 156 | impl FunctorOrd for BTreeSet { 157 | type Target = BTreeSet; 158 | 159 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 160 | self.into_iter().map(f).collect() 161 | } 162 | 163 | fn __try_fmap_0_ref( 164 | self, 165 | f: &impl Fn(A) -> Result, 166 | ) -> Result, E> { 167 | self.into_iter().map(f).collect() 168 | } 169 | } 170 | 171 | #[doc(hidden)] 172 | pub trait FunctorOrd: Sized { 173 | type Target; 174 | 175 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 176 | self.__fmap_0_ref(&f) 177 | } 178 | 179 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 180 | self.__try_fmap_0_ref(&f) 181 | } 182 | 183 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target; 184 | 185 | fn __try_fmap_0_ref( 186 | self, 187 | f: &impl Fn(A) -> Result, 188 | ) -> Result, E>; 189 | } 190 | 191 | impl FunctorOrd for BinaryHeap { 192 | type Target = BinaryHeap; 193 | 194 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 195 | self.into_iter().map(f).collect() 196 | } 197 | 198 | fn __try_fmap_0_ref( 199 | self, 200 | f: &impl Fn(A) -> Result, 201 | ) -> Result, E> { 202 | self.into_iter().map(f).collect() 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /functor_derive/src/impl_core.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use core::cell::{Cell, RefCell, UnsafeCell}; 3 | use core::marker::PhantomData; 4 | use core::mem::MaybeUninit; 5 | use core::ops::ControlFlow; 6 | use core::{mem, ptr}; 7 | 8 | functor_impl!(Option); 9 | 10 | impl Functor0 for Option { 11 | type Target = Option; 12 | 13 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 14 | self.map(f) 15 | } 16 | 17 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 18 | self.map(f).transpose() 19 | } 20 | } 21 | 22 | impl Functor for Result { 23 | type Target = Result; 24 | 25 | /// By default Results map their Ok generic. 26 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 27 | self.__fmap_0_ref(&f) 28 | } 29 | 30 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E2> { 31 | self.__try_fmap_0_ref(&f) 32 | } 33 | } 34 | 35 | impl Functor0 for Result { 36 | type Target = Result; 37 | 38 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 39 | self.map(f) 40 | } 41 | 42 | fn __try_fmap_0_ref( 43 | self, 44 | f: &impl Fn(A) -> Result, 45 | ) -> Result, E2> { 46 | match self.map(f) { 47 | Ok(Ok(v)) => Ok(Ok(v)), 48 | Ok(Err(e)) => Err(e), 49 | Err(e) => Ok(Err(e)), 50 | } 51 | } 52 | } 53 | 54 | impl Functor1 for Result { 55 | type Target = Result; 56 | 57 | fn __fmap_1_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 58 | self.map_err(f) 59 | } 60 | 61 | fn __try_fmap_1_ref( 62 | self, 63 | f: &impl Fn(A) -> Result, 64 | ) -> Result, E2> { 65 | match self.map_err(f) { 66 | Ok(v) => Ok(Ok(v)), 67 | Err(Ok(e)) => Ok(Err(e)), 68 | Err(Err(e)) => Err(e), 69 | } 70 | } 71 | } 72 | 73 | functor_impl!(PhantomData); 74 | 75 | impl Functor0 for PhantomData { 76 | type Target = PhantomData; 77 | 78 | fn __fmap_0_ref(self, _f: &impl Fn(A) -> B) -> Self::Target { 79 | PhantomData 80 | } 81 | 82 | fn __try_fmap_0_ref(self, _f: &impl Fn(A) -> Result) -> Result, E> { 83 | Ok(PhantomData) 84 | } 85 | } 86 | 87 | impl Functor for [A; N] { 88 | type Target = [B; N]; 89 | 90 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 91 | self.__fmap_0_ref(&f) 92 | } 93 | 94 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 95 | self.__try_fmap_0_ref(&f) 96 | } 97 | } 98 | 99 | impl Functor0 for [A; N] { 100 | type Target = [B; N]; 101 | 102 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 103 | self.map(f) 104 | } 105 | 106 | // This implementation was provided by Matthias Stemmler's crate [funcmap_derive](https://crates.io/crates/funcmap_derive) under the MIT license. 107 | // 108 | // Licensed under either of 109 | // * Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0) 110 | // * MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT) 111 | // at your option. 112 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 113 | // This guards the target array, making sure the part of it that has already 114 | // been filled is dropped if `f` returns `Err(_)` or panics 115 | struct Guard<'a, T, const N: usize> { 116 | // mutable borrow of the target array 117 | array_mut: &'a mut [MaybeUninit; N], 118 | // index in ..=N up to which (exclusive) `array_mut` is initialized 119 | init_until_idx: usize, 120 | } 121 | 122 | impl Drop for Guard<'_, T, N> { 123 | fn drop(&mut self) { 124 | // - `self.init_until_idx <= N` is always satisfied 125 | // - if `self.init_until_idx == N`, the target array is fully 126 | // initialized and hence the guard must not be dropped 127 | debug_assert!(self.init_until_idx < N); 128 | 129 | // SAFETY: as `self.init_until_idx <= N`, the range is within bounds of `self.array_mut` 130 | let init_slice = unsafe { self.array_mut.get_unchecked_mut(..self.init_until_idx) }; 131 | 132 | // SAFETY: by definition of `init_until_idx`, `init_slice` is fully initialized 133 | let init_slice = unsafe { &mut *(init_slice as *mut [MaybeUninit]).cast::() }; 134 | 135 | // SAFETY: 136 | // - `init_slice` is valid for dropping 137 | // - `self.array_mut` (and hence `init_slice`) is not used after `self` is dropped 138 | unsafe { ptr::drop_in_place(init_slice) }; 139 | } 140 | } 141 | 142 | // This can be replaced with a call to `MaybeUninit::uninit_array` once that is stabilized 143 | // 144 | // SAFETY: an array of `MaybeUninit<_>` is always initialized 145 | let mut mapped: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; 146 | 147 | let mut guard = Guard { 148 | array_mut: &mut mapped, 149 | init_until_idx: 0, 150 | }; 151 | 152 | for value in self { 153 | // SAFETY: the iterator yields exactly `N` elements, 154 | // so `guard.init_until_idx` has been increased at most `N - 1` times 155 | // and hence is within bounds of `guard.array_mut` 156 | unsafe { 157 | guard 158 | .array_mut 159 | .get_unchecked_mut(guard.init_until_idx) 160 | // if `f` returns `Err(_)` or panics, then `guard` is dropped 161 | .write(f(value)?); 162 | } 163 | 164 | guard.init_until_idx += 1; 165 | } 166 | 167 | // now `guard.init_until_idx == N` and the target array is fully initialized, 168 | // so make sure the guard isn't dropped 169 | mem::forget(guard); 170 | 171 | // SAFETY: `mapped` is fully initialized 172 | let mapped = unsafe { ptr::addr_of!(mapped).cast::<[B; N]>().read() }; 173 | 174 | Ok(mapped) 175 | } 176 | } 177 | 178 | functor_impl!(Cell); 179 | 180 | impl Functor0 for Cell { 181 | type Target = Cell; 182 | 183 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 184 | Cell::new(f(self.into_inner())) 185 | } 186 | 187 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 188 | f(self.into_inner()).map(Cell::new) 189 | } 190 | } 191 | 192 | functor_impl!(RefCell); 193 | 194 | impl Functor0 for RefCell { 195 | type Target = RefCell; 196 | 197 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 198 | RefCell::new(f(self.into_inner())) 199 | } 200 | 201 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 202 | f(self.into_inner()).map(RefCell::new) 203 | } 204 | } 205 | 206 | functor_impl!(UnsafeCell); 207 | 208 | impl Functor0 for UnsafeCell { 209 | type Target = UnsafeCell; 210 | 211 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 212 | UnsafeCell::new(f(self.into_inner())) 213 | } 214 | 215 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 216 | f(self.into_inner()).map(UnsafeCell::new) 217 | } 218 | } 219 | 220 | impl Functor for ControlFlow { 221 | type Target = ControlFlow; 222 | 223 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 224 | self.__fmap_0_ref(&f) 225 | } 226 | 227 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 228 | self.__try_fmap_0_ref(&f) 229 | } 230 | } 231 | 232 | impl Functor0 for ControlFlow { 233 | type Target = ControlFlow; 234 | 235 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 236 | match self { 237 | ControlFlow::Continue(c) => ControlFlow::Continue(c), 238 | ControlFlow::Break(v) => ControlFlow::Break(f(v)), 239 | } 240 | } 241 | 242 | fn __try_fmap_0_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 243 | Ok(match self { 244 | ControlFlow::Continue(c) => ControlFlow::Continue(c), 245 | ControlFlow::Break(v) => ControlFlow::Break(f(v)?), 246 | }) 247 | } 248 | } 249 | 250 | impl Functor1 for ControlFlow { 251 | type Target = ControlFlow; 252 | 253 | fn __fmap_1_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 254 | match self { 255 | ControlFlow::Continue(v) => ControlFlow::Continue(f(v)), 256 | ControlFlow::Break(c) => ControlFlow::Break(c), 257 | } 258 | } 259 | 260 | fn __try_fmap_1_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 261 | Ok(match self { 262 | ControlFlow::Continue(v) => ControlFlow::Continue(f(v)?), 263 | ControlFlow::Break(c) => ControlFlow::Break(c), 264 | }) 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /functor_derive/src/impl_std.rs: -------------------------------------------------------------------------------- 1 | use crate::{Functor, Functor1, FunctorValues}; 2 | use std::collections::{HashMap, HashSet}; 3 | use std::hash::Hash; 4 | 5 | #[doc(hidden)] 6 | pub trait FunctorHashKeys: Sized { 7 | type Target; 8 | 9 | fn fmap_keys(self, f: impl Fn(A) -> B) -> Self::Target { 10 | self.__fmap_0_ref(&f) 11 | } 12 | 13 | fn try_fmap_keys( 14 | self, 15 | f: impl Fn(A) -> Result, 16 | ) -> Result, E> { 17 | self.__try_fmap_0_ref(&f) 18 | } 19 | 20 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target; 21 | 22 | fn __try_fmap_0_ref( 23 | self, 24 | f: &impl Fn(A) -> Result, 25 | ) -> Result, E>; 26 | } 27 | 28 | #[doc(hidden)] 29 | pub trait FunctorHashSet: Sized { 30 | type Target; 31 | 32 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 33 | self.__fmap_0_ref(&f) 34 | } 35 | 36 | fn try_fmap( 37 | self, 38 | f: impl Fn(A) -> Result, 39 | ) -> Result, E> { 40 | self.__try_fmap_0_ref(&f) 41 | } 42 | 43 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target; 44 | 45 | fn __try_fmap_0_ref( 46 | self, 47 | f: &impl Fn(A) -> Result, 48 | ) -> Result, E>; 49 | } 50 | 51 | impl FunctorHashSet for HashSet { 52 | type Target = HashSet; 53 | 54 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 55 | self.into_iter().map(f).collect() 56 | } 57 | 58 | fn __try_fmap_0_ref( 59 | self, 60 | f: &impl Fn(A) -> Result, 61 | ) -> Result, E> { 62 | self.into_iter().map(f).collect() 63 | } 64 | } 65 | 66 | impl FunctorHashKeys for HashMap { 67 | type Target = HashMap; 68 | 69 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 70 | self.into_iter().map(|(k, v)| (f(k), v)).collect() 71 | } 72 | 73 | fn __try_fmap_0_ref( 74 | self, 75 | f: &impl Fn(A) -> Result, 76 | ) -> Result, E> { 77 | self.into_iter() 78 | .map(|(k, v)| f(k).map(|k| (k, v))) 79 | .collect() 80 | } 81 | } 82 | 83 | impl Functor1 for HashMap { 84 | type Target = HashMap; 85 | 86 | fn __fmap_1_ref(self, f: &impl Fn(A) -> B) -> Self::Target { 87 | self.into_iter().map(|(k, v)| (k, f(v))).collect() 88 | } 89 | 90 | fn __try_fmap_1_ref(self, f: &impl Fn(A) -> Result) -> Result, E> { 91 | self.into_iter() 92 | .map(|(k, v)| f(v).map(|v| (k, v))) 93 | .collect() 94 | } 95 | } 96 | 97 | impl Functor for HashMap { 98 | type Target = HashMap; 99 | 100 | /// By default HashMaps map their Value generic. 101 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 102 | self.__fmap_1_ref(&f) 103 | } 104 | 105 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 106 | self.__try_fmap_1_ref(&f) 107 | } 108 | } 109 | 110 | impl FunctorValues for HashMap { 111 | type Target = HashMap; 112 | 113 | fn fmap_values(self, f: impl Fn(A) -> B) -> Self::Target { 114 | self.__fmap_1_ref(&f) 115 | } 116 | 117 | fn try_fmap_values(self, f: impl Fn(A) -> Result) -> Result, E> { 118 | self.__try_fmap_1_ref(&f) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /functor_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod impl_alloc; 2 | pub mod impl_core; 3 | pub mod impl_std; 4 | 5 | use paste::paste; 6 | 7 | // Re-export derive macro. 8 | pub use functor_derive_lib::*; 9 | pub use impl_alloc::*; 10 | #[allow(unused)] 11 | pub use impl_core::*; 12 | pub use impl_std::*; 13 | 14 | pub trait Functor: Sized { 15 | type Target; 16 | 17 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target; 18 | 19 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E>; 20 | } 21 | 22 | #[macro_export] 23 | macro_rules! functor_n { 24 | ($n:expr) => { 25 | paste! { 26 | #[doc(hidden)] 27 | pub trait []: Sized { 28 | type Target; 29 | 30 | fn [<__fmap_ $n _ref>](self, f: &impl Fn(A) -> B) -> Self::Target; 31 | 32 | fn [<__try_fmap_ $n _ref>](self, f: &impl Fn(A) -> Result) -> Result, E>; 33 | } 34 | } 35 | }; 36 | } 37 | 38 | functor_n!(0); 39 | functor_n!(1); 40 | functor_n!(2); 41 | functor_n!(3); 42 | functor_n!(4); 43 | functor_n!(5); 44 | functor_n!(6); 45 | functor_n!(7); 46 | functor_n!(8); 47 | functor_n!(9); 48 | functor_n!(10); 49 | functor_n!(11); 50 | functor_n!(12); 51 | functor_n!(13); 52 | functor_n!(14); 53 | functor_n!(15); 54 | functor_n!(16); 55 | functor_n!(17); 56 | functor_n!(18); 57 | functor_n!(19); 58 | // please don't use more than 20 generics. 59 | 60 | #[doc(hidden)] 61 | pub trait FunctorValues: Sized { 62 | type Target; 63 | 64 | fn fmap_values(self, f: impl Fn(A) -> B) -> Self::Target; 65 | 66 | fn try_fmap_values(self, f: impl Fn(A) -> Result) -> Result, E>; 67 | } 68 | 69 | #[doc(hidden)] 70 | pub trait FunctorOrdKeys: Sized { 71 | type Target; 72 | 73 | fn fmap_keys(self, f: impl Fn(A) -> B) -> Self::Target { 74 | self.__fmap_0_ref(&f) 75 | } 76 | 77 | fn try_fmap_keys(self, f: impl Fn(A) -> Result) -> Result, E> { 78 | self.__try_fmap_0_ref(&f) 79 | } 80 | 81 | fn __fmap_0_ref(self, f: &impl Fn(A) -> B) -> Self::Target; 82 | 83 | fn __try_fmap_0_ref( 84 | self, 85 | f: &impl Fn(A) -> Result, 86 | ) -> Result, E>; 87 | } 88 | 89 | #[macro_export] 90 | macro_rules! functor_impl { 91 | ($typ:ident) => { 92 | paste::paste! { 93 | impl Functor for $typ { 94 | type Target = $typ; 95 | 96 | fn fmap(self, f: impl Fn(A) -> B) -> Self::Target { 97 | self.[<__fmap_0_ref>](&f) 98 | } 99 | 100 | fn try_fmap(self, f: impl Fn(A) -> Result) -> Result, E> { 101 | self.[<__try_fmap_0_ref>](&f) 102 | } 103 | } 104 | } 105 | }; 106 | } 107 | -------------------------------------------------------------------------------- /functor_derive/tests/attributes.rs: -------------------------------------------------------------------------------- 1 | use functor_derive::Functor; 2 | use std::any::{Any, TypeId}; 3 | 4 | // attribute with default, 5 | // attribute with name_map, 6 | // attribute with default and name_map 7 | 8 | #[test] 9 | fn default_single() { 10 | #[derive(Functor)] 11 | #[functor(S)] 12 | struct MyType { 13 | v1: S, 14 | v2: bool, 15 | } 16 | 17 | let x = MyType { 18 | v1: 42usize, 19 | v2: true, 20 | }; 21 | 22 | assert_eq!(x.fmap(|x| x as u64).type_id(), TypeId::of::>()); 23 | } 24 | 25 | #[test] 26 | fn default_multiple_out_of_order() { 27 | #[derive(Functor)] 28 | #[functor(T)] 29 | struct MyType { 30 | v1: S, 31 | v2: T, 32 | } 33 | 34 | let x = MyType { 35 | v1: true, 36 | v2: 18usize, 37 | }; 38 | 39 | assert_eq!( 40 | x.fmap(|x| x as u64).type_id(), 41 | TypeId::of::>() 42 | ); 43 | } 44 | 45 | #[test] 46 | fn map_specified_and_name() { 47 | #[derive(Copy, Clone, Functor)] 48 | #[functor(S, S as stuff, S as gunk)] 49 | struct MyType { 50 | v1: S, 51 | v2: bool, 52 | } 53 | 54 | let x = MyType { 55 | v1: 42usize, 56 | v2: true, 57 | }; 58 | 59 | assert_eq!(x.fmap(|x| x as u64).type_id(), TypeId::of::>()); 60 | 61 | assert_eq!( 62 | x.fmap_stuff(|x| x as u64).type_id(), 63 | TypeId::of::>() 64 | ); 65 | 66 | assert_eq!( 67 | x.fmap_gunk(|x| x as u64).type_id(), 68 | TypeId::of::>() 69 | ); 70 | } 71 | 72 | #[test] 73 | fn map_multi() { 74 | #[derive(Copy, Clone, Functor)] 75 | #[functor(T, S as wonky)] 76 | struct MyType { 77 | v1: S, 78 | v2: bool, 79 | v3: T, 80 | } 81 | 82 | let x = MyType { 83 | v1: 42usize, 84 | v2: true, 85 | v3: false, 86 | }; 87 | 88 | assert_eq!( 89 | x.fmap(|x| x as u64).type_id(), 90 | TypeId::of::>() 91 | ); 92 | 93 | assert_eq!( 94 | x.fmap_wonky(|x| x as u64).type_id(), 95 | TypeId::of::>() 96 | ); 97 | } 98 | -------------------------------------------------------------------------------- /functor_derive/tests/bounds.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | 3 | use functor_derive::Functor; 4 | use std::any::{Any, TypeId}; 5 | use std::fmt::Display; 6 | 7 | #[test] 8 | fn trait_bound() { 9 | #[derive(Functor)] 10 | struct StructSimple { 11 | field_1: A, 12 | } 13 | 14 | let x = StructSimple:: { field_1: 42 }; 15 | 16 | assert_eq!( 17 | x.fmap(|x| x as u64).type_id(), 18 | TypeId::of::>() 19 | ); 20 | } 21 | 22 | #[test] 23 | fn trait_bound_named() { 24 | #[derive(Functor)] 25 | #[functor(A as apple)] 26 | struct StructSimple { 27 | field_1: A, 28 | } 29 | 30 | let x = StructSimple:: { field_1: 42 }; 31 | 32 | assert_eq!( 33 | x.fmap_apple(|x| x as u64).type_id(), 34 | TypeId::of::>() 35 | ); 36 | } 37 | 38 | #[test] 39 | fn trait_ignored() { 40 | #[derive(Functor)] 41 | #[functor(A as apple)] 42 | struct StructSimple { 43 | field_1: A, 44 | field_2: B, 45 | } 46 | 47 | let x = StructSimple:: { 48 | field_1: 42, 49 | field_2: 43, 50 | }; 51 | 52 | assert_eq!( 53 | x.fmap_apple(|x| x as u64).type_id(), 54 | TypeId::of::>() 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap.rs: -------------------------------------------------------------------------------- 1 | #[path = "funcmap/mod.rs"] 2 | mod funcmap; 3 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/decorations.rs: -------------------------------------------------------------------------------- 1 | use crate::funcmap::{T1, T2}; 2 | use functor_derive::Functor; 3 | use std::marker::PhantomData; 4 | 5 | #[test] 6 | fn attributes_on_type_params_are_supported() { 7 | #[derive(Functor, Debug, PartialEq)] 8 | struct Test<#[cfg(test)] S, #[cfg(test)] T>(S, T); 9 | 10 | let src = Test(T1, T1); 11 | let dst = src.fmap(|_| T2); 12 | 13 | assert_eq!(dst, Test(T2, T1)); 14 | } 15 | 16 | #[test] 17 | fn attributes_on_lifetime_params_are_supported() { 18 | #[derive(Functor, Debug, PartialEq)] 19 | struct Test<#[cfg(test)] 'a, T>(T, PhantomData<&'a ()>); 20 | 21 | let src = Test(T1, PhantomData); 22 | let dst = src.fmap(|_| T2); 23 | 24 | assert_eq!(dst, Test(T2, PhantomData)); 25 | } 26 | 27 | #[test] 28 | fn attributes_on_const_params_are_supported() { 29 | #[derive(Functor, Debug, PartialEq)] 30 | struct Test(T); 31 | 32 | let src = Test::<_, 42>(T1); 33 | let dst = src.fmap(|_| T2); 34 | 35 | assert_eq!(dst, Test(T2)); 36 | } 37 | 38 | #[test] 39 | fn defaults_on_type_params_are_supported() { 40 | #[derive(Functor, Debug, PartialEq)] 41 | struct Test(S, T); 42 | 43 | let src = Test(T1, T1); 44 | let dst = src.fmap(|_| T2); 45 | 46 | assert_eq!(dst, Test(T2, T1)); 47 | } 48 | 49 | #[test] 50 | fn defaults_on_const_params_are_supported() { 51 | #[derive(Functor, Debug, PartialEq)] 52 | struct Test(T); 53 | 54 | let src = Test::<_>(T1); 55 | let dst = src.fmap(|_| T2); 56 | 57 | assert_eq!(dst, Test(T2)); 58 | } 59 | 60 | #[test] 61 | fn impl_is_restricted_to_trait_bounds_on_generics_of_original_type() { 62 | trait TestTrait {} 63 | 64 | impl TestTrait for T1 {} 65 | impl TestTrait for T2 {} 66 | 67 | // derived impl is supposed to have the corresponding trait bounds 68 | #[derive(Functor, Debug, PartialEq)] 69 | struct Test(S, T); 70 | 71 | let src = Test(T1, T1); 72 | let dst = src.fmap(|_| T2); 73 | 74 | assert_eq!(dst, Test(T2, T1)); 75 | } 76 | 77 | #[test] 78 | fn impl_is_restricted_to_self_dependent_trait_bounds_on_generics_of_original_type() { 79 | trait TestTrait { 80 | type Assoc; 81 | } 82 | 83 | impl TestTrait for T1 { 84 | type Assoc = S; 85 | } 86 | 87 | impl TestTrait for T2 { 88 | type Assoc = S; 89 | } 90 | 91 | // derived impl is supposed to have the corresponding trait bounds 92 | #[derive(Functor, Debug, PartialEq)] 93 | struct Test, T: TestTrait>(S, T); 94 | 95 | let src = Test(T1, T1); 96 | let dst = src.fmap(|_| T2); 97 | 98 | assert_eq!(dst, Test(T2, T1)); 99 | } 100 | 101 | #[test] 102 | fn impl_is_restricted_to_cross_dependent_trait_bounds_on_generics_of_original_type() { 103 | trait TestTrait { 104 | type Assoc; 105 | } 106 | 107 | impl TestTrait for T1 { 108 | type Assoc = S; 109 | } 110 | 111 | impl TestTrait for T2 { 112 | type Assoc = S; 113 | } 114 | 115 | // derived impl is supposed to have the corresponding trait bounds 116 | #[derive(Functor, Debug, PartialEq)] 117 | struct Test, T: TestTrait>(S, T); 118 | 119 | let src = Test(T1, T1); 120 | let dst = src.fmap(|_| T2); 121 | 122 | assert_eq!(dst, Test(T2, T1)); 123 | } 124 | 125 | #[test] 126 | fn impl_is_restricted_to_maybe_sized_bound_on_unmapped_generic_of_original_type() { 127 | trait TestTrait {} 128 | 129 | type Unsized = [()]; 130 | 131 | impl TestTrait for Unsized {} 132 | impl TestTrait for Unsized {} 133 | 134 | // derived impl for mapping over S is supposed to have no `S: ?Sized` (more 135 | // precisely, no `A: ?Sized` and `B: ?Sized` where `A` and `B` are the 136 | // generic source and target types) but `T: ?Sized` (even though the bound 137 | // for T depends on S) 138 | #[derive(Functor, Debug, PartialEq)] 139 | struct Test>(PhantomData, PhantomData); 140 | 141 | let src = Test::(PhantomData, PhantomData); 142 | let dst = src.fmap(|_: T1| T2); 143 | 144 | assert_eq!(dst, Test(PhantomData, PhantomData)); 145 | } 146 | 147 | #[test] 148 | fn impl_is_restricted_to_lifetime_bounds_on_generics_of_original_type() { 149 | // derived impl is supposed to have the corresponding lifetime bounds 150 | #[derive(Functor, Debug, PartialEq)] 151 | struct Test<'a, T: 'a>(T, PhantomData<&'a ()>); 152 | 153 | let src = Test(T1, PhantomData); 154 | let dst = src.fmap(|_| T2); 155 | 156 | assert_eq!(dst, Test(T2, PhantomData)); 157 | } 158 | 159 | #[test] 160 | fn impl_is_restricted_to_trait_bounds_in_where_clause_on_original_type() { 161 | trait TestTrait {} 162 | 163 | impl TestTrait for T1 {} 164 | impl TestTrait for T2 {} 165 | 166 | // derived impl is supposed to have the corresponding where predicates 167 | #[derive(Functor, Debug, PartialEq)] 168 | struct Test(S, T) 169 | where 170 | S: TestTrait, 171 | T: TestTrait; 172 | 173 | let src = Test(T1, T1); 174 | let dst = src.fmap(|_| T2); 175 | 176 | assert_eq!(dst, Test(T2, T1)); 177 | } 178 | 179 | #[test] 180 | fn impl_is_restricted_to_self_dependent_trait_bounds_in_where_clause_on_original_type() { 181 | trait TestTrait { 182 | type Assoc; 183 | } 184 | 185 | impl TestTrait for T1 { 186 | type Assoc = S; 187 | } 188 | 189 | impl TestTrait for T2 { 190 | type Assoc = S; 191 | } 192 | 193 | // derived impl is supposed to have the corresponding where predicates 194 | #[derive(Functor, Debug, PartialEq)] 195 | struct Test(S, T) 196 | where 197 | S: TestTrait, 198 | T: TestTrait; 199 | 200 | let src = Test(T1, T1); 201 | let dst = src.fmap(|_| T2); 202 | 203 | assert_eq!(dst, Test(T2, T1)); 204 | } 205 | 206 | #[test] 207 | fn impl_is_restricted_to_cross_dependent_trait_bounds_in_where_clause_on_original_type() { 208 | trait TestTrait { 209 | type Assoc; 210 | } 211 | 212 | impl TestTrait for T1 { 213 | type Assoc = S; 214 | } 215 | 216 | impl TestTrait for T2 { 217 | type Assoc = S; 218 | } 219 | 220 | // derived impl is supposed to have the corresponding where predicates 221 | #[derive(Functor, Debug, PartialEq)] 222 | struct Test(S, T) 223 | where 224 | S: TestTrait, 225 | T: TestTrait; 226 | 227 | let src = Test(T1, T1); 228 | let dst = src.fmap(|_| T2); 229 | 230 | assert_eq!(dst, Test(T2, T1)); 231 | } 232 | 233 | // todo 234 | // #[test] 235 | // fn impl_is_restricted_to_arbitrary_trait_bounds_in_where_clause_on_original_type() { 236 | // trait TestTrait { 237 | // type Assoc; 238 | // } 239 | // 240 | // impl TestTrait for T1 { 241 | // type Assoc = S; 242 | // } 243 | // 244 | // impl TestTrait for T2 { 245 | // type Assoc = S; 246 | // } 247 | // 248 | // // derived impl is supposed to have the corresponding where predicates 249 | // #[derive(Functor, Debug, PartialEq)] 250 | // struct Test(S, T) 251 | // where 252 | // S: TestTrait, 253 | // >::Assoc: TestTrait, 254 | // <>::Assoc as TestTrait>::Assoc: TestTrait; 255 | // 256 | // let src = Test(T1, T1); 257 | // let dst = src.fmap(|_| T2); 258 | // 259 | // assert_eq!(dst, Test(T2, T1)); 260 | // } 261 | 262 | #[test] 263 | fn impl_is_restricted_to_trait_bounds_with_bound_lifetimes_in_where_clause_on_original_type() { 264 | trait TestTrait<'a> {} 265 | 266 | impl<'a> TestTrait<'a> for T1 {} 267 | impl<'a> TestTrait<'a> for T2 {} 268 | 269 | // derived impl is supposed to have the corresponding where predicates 270 | #[derive(Functor, Debug, PartialEq)] 271 | struct Test(S, T) 272 | where 273 | for<'a> T: TestTrait<'a>; 274 | 275 | let src = Test(T1, T1); 276 | let dst = src.fmap(|_| T2); 277 | 278 | assert_eq!(dst, Test(T2, T1)); 279 | } 280 | 281 | // todo 282 | // #[test] 283 | // fn impl_is_restricted_to_maybe_sized_bound_on_unmapped_generic_in_where_clause_of_original_type() { 284 | // trait TestTrait {} 285 | // 286 | // type Unsized = [()]; 287 | // 288 | // impl TestTrait for Unsized {} 289 | // impl TestTrait for Unsized {} 290 | // 291 | // // derived impl for mapping over S is supposed to have no `S: ?Sized` (more 292 | // // precisely, no `A: ?Sized` and `B: ?Sized` where `A` and `B` are the 293 | // // generic source and target types) but `T: ?Sized` (even though the bound 294 | // // for T depends on S) 295 | // #[derive(Functor, Debug, PartialEq)] 296 | // struct Test(PhantomData, PhantomData) 297 | // where 298 | // S: ?Sized, 299 | // T: ?Sized + TestTrait; 300 | // 301 | // let src = Test::(PhantomData, PhantomData); 302 | // let dst = src.fmap(|_: T1| T2); 303 | // 304 | // assert_eq!(dst, Test(PhantomData, PhantomData)); 305 | // } 306 | 307 | #[test] 308 | fn impl_is_restricted_to_lifetime_bounds_in_where_clause_of_original_type() { 309 | // derived impl is supposed to have the corresponding where predicates 310 | #[derive(Functor, Debug, PartialEq)] 311 | struct Test<'a, T>(T, PhantomData<&'a ()>) 312 | where 313 | T: 'a; 314 | 315 | let src = Test(T1, PhantomData); 316 | let dst = src.fmap(|_| T2); 317 | 318 | assert_eq!(dst, Test(T2, PhantomData)); 319 | } 320 | 321 | // Test is too different from how we construct Functors. 322 | // #[test] 323 | // fn impl_is_restricted_to_allow_mapping_of_inner_type() { 324 | // #[derive(Debug, PartialEq)] 325 | // struct Inner(T); 326 | // 327 | // // impl manually only for Inner, not generic Inner 328 | // impl Functor for Inner { 329 | // type Output = Inner; 330 | // 331 | // fn fmap(self, _: F) -> Self::Output 332 | // where 333 | // F: FnMut(T1) -> T2, 334 | // { 335 | // Inner(T2) 336 | // } 337 | // } 338 | // 339 | // // derived impl is supposed to have 340 | // // `Inner: Functor>` 341 | // #[derive(Functor, Debug, PartialEq)] 342 | // struct Test(Inner); 343 | // 344 | // let src = Test(Inner(T1)); 345 | // let dst = src.fmap(|_| T2); 346 | // 347 | // assert_eq!(dst, Test(Inner(T2))); 348 | // } 349 | 350 | // todo 351 | // #[test] 352 | // fn impl_is_restricted_to_sized_bound_on_unmapped_inner_type() { 353 | // trait TestTrait { 354 | // type Assoc: ?Sized; 355 | // } 356 | // 357 | // type Unsized = [()]; 358 | // 359 | // impl TestTrait for T1 { 360 | // type Assoc = (); 361 | // } 362 | // 363 | // impl TestTrait for T2 { 364 | // type Assoc = Unsized; 365 | // } 366 | // 367 | // // derived impl for mapping over S is supposed to have `T::Assoc: Sized`, so 368 | // // `Test` implements `Functor` but `Test` doesn't 369 | // #[derive(Functor, Debug, PartialEq)] 370 | // #[functor(S)] 371 | // struct Test(S, T::Assoc) 372 | // where 373 | // T: TestTrait; 374 | // 375 | // let src: Test = Test(T1, ()); 376 | // let dst = src.fmap(|_| T2); 377 | // 378 | // assert_eq!(dst, Test(T2, ())); 379 | // } 380 | 381 | #[test] 382 | fn const_args_with_braces_are_supported() { 383 | #[derive(Debug, PartialEq)] 384 | struct Inner; 385 | 386 | #[derive(Functor, Debug, PartialEq)] 387 | struct Test(T, Inner<{ 41 + 1 }>); 388 | 389 | let src = Test(T1, Inner); 390 | let dst = src.fmap(|_| T2); 391 | 392 | assert_eq!(dst, Test(T2, Inner)); 393 | } 394 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/fallible.rs: -------------------------------------------------------------------------------- 1 | use functor_derive::Functor; 2 | use std::convert::{TryFrom, TryInto}; 3 | 4 | #[derive(Debug, PartialEq)] 5 | enum T1 { 6 | Mappable, 7 | NotMappable(&'static str), 8 | } 9 | 10 | #[derive(Debug, PartialEq)] 11 | struct T2; 12 | 13 | #[derive(Debug, PartialEq)] 14 | struct MappingError(&'static str); 15 | 16 | #[test] 17 | fn mapping_succeeds_when_function_succeeds() { 18 | #[derive(Functor, Debug, PartialEq)] 19 | struct Test(T, T, T); 20 | 21 | let src = Test(T1::Mappable, T1::Mappable, T1::Mappable); 22 | let dst: Result, _> = src.try_fmap(TryInto::try_into); 23 | 24 | assert_eq!(dst, Ok(Test(T2, T2, T2))); 25 | } 26 | 27 | #[test] 28 | fn mapping_fails_with_first_error_when_function_fails() { 29 | #[derive(Functor, Debug, PartialEq)] 30 | struct Test(T, T, T); 31 | 32 | let src = Test( 33 | T1::NotMappable("First Error"), 34 | T1::Mappable, 35 | T1::NotMappable("Second Error"), 36 | ); 37 | let dst: Result, _> = src.try_fmap(TryInto::try_into); 38 | 39 | assert_eq!(dst, Err(MappingError("First Error"))); 40 | } 41 | 42 | impl TryFrom for T2 { 43 | type Error = MappingError; 44 | 45 | fn try_from(value: T1) -> Result { 46 | match value { 47 | T1::Mappable => Ok(T2), 48 | T1::NotMappable(message) => Err(MappingError(message)), 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/hygiene.rs: -------------------------------------------------------------------------------- 1 | use functor_derive_lib::Functor; 2 | 3 | #[test] 4 | fn conflicting_type_params_are_avoided() { 5 | #[allow(non_snake_case)] 6 | #[allow(unused)] 7 | #[derive(Functor)] 8 | struct A { 9 | D: (), 10 | B: B, 11 | F: F, 12 | } 13 | } 14 | 15 | #[test] 16 | fn fields_conflicting_with_items_are_supported() { 17 | #[allow(non_snake_case)] 18 | #[derive(Functor)] 19 | struct Test { 20 | funcmap: T, 21 | FuncMap: T, 22 | TypeParam: T, 23 | Output: T, 24 | func_map: T, 25 | f: T, 26 | } 27 | } 28 | 29 | #[test] 30 | fn nested_items_are_not_mistaken_for_generics() { 31 | mod test { 32 | pub struct T; 33 | } 34 | 35 | #[derive(Functor)] 36 | struct Test(T, test::T); 37 | } 38 | 39 | #[test] 40 | fn inherent_methods_are_not_mistaken_for_trait_methods() { 41 | #[derive(Functor)] 42 | struct Inner(T); 43 | 44 | #[allow(dead_code)] 45 | impl Inner { 46 | fn func_map(self) {} 47 | fn func_map_over(self) {} 48 | } 49 | 50 | #[derive(Functor)] 51 | struct Test(Inner); 52 | } 53 | 54 | #[test] 55 | fn trait_methods_are_not_confused_with_methods_of_other_traits() { 56 | #[derive(Functor)] 57 | struct Inner(T); 58 | 59 | trait LikeFuncMap: Sized { 60 | fn func_map(self) {} 61 | fn func_map_over(self) {} 62 | } 63 | 64 | impl LikeFuncMap for Inner {} 65 | 66 | impl LikeFuncMap for [T; N] {} 67 | 68 | #[derive(Functor)] 69 | struct Test(Inner, [T; N]); 70 | } 71 | 72 | #[test] 73 | fn raw_identifiers_are_supported() { 74 | #![allow(non_camel_case_types)] 75 | #![allow(non_upper_case_globals)] 76 | 77 | use functor_derive as r#break; 78 | 79 | #[derive(Functor)] 80 | struct r#continue(r#else); 81 | 82 | #[derive(r#break::Functor)] 83 | enum r#false { 84 | r#if { r#in: r#continue }, 85 | r#let, 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/impl_alloc.rs: -------------------------------------------------------------------------------- 1 | use crate::funcmap::{T1, T2}; 2 | use functor_derive::Functor; 3 | use std::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}; 4 | use std::vec; 5 | 6 | #[test] 7 | fn field_of_binary_heap_type_is_mapped() { 8 | #[derive(Functor, Debug)] 9 | struct Test(BinaryHeap); 10 | 11 | let src = Test([T1].into()); 12 | let dst = src.fmap(|_| T2); 13 | 14 | assert_eq!(dst.0.into_vec(), [T2]); 15 | } 16 | 17 | // todo: not implemented 18 | // #[test] 19 | // fn field_of_binary_heap_into_iter_type_is_mapped() { 20 | // #[derive(Functor, Debug)] 21 | // struct Test(binary_heap::IntoIter); 22 | // 23 | // let src = Test(BinaryHeap::from([T1]).into_iter()); 24 | // let dst = src.fmap(|_| T2); 25 | // 26 | // assert_eq!(dst.0.collect::>(), [T2]); 27 | // } 28 | 29 | #[test] 30 | fn field_of_box_type_is_mapped() { 31 | #[derive(Functor, Debug, PartialEq)] 32 | struct Test(Box); 33 | 34 | let src = Test(Box::new(T1)); 35 | let dst = src.fmap(|_| T2); 36 | 37 | assert_eq!(dst, Test(Box::new(T2))); 38 | } 39 | 40 | #[test] 41 | fn field_of_btree_map_type_is_mapped_over_key() { 42 | #[derive(Functor, Debug, PartialEq)] 43 | struct Test(BTreeMap); 44 | 45 | let src = Test([(T1, ())].into()); 46 | let dst = src.fmap(|_| T2); 47 | 48 | assert_eq!(dst, Test([(T2, ())].into())); 49 | } 50 | 51 | #[test] 52 | fn field_of_btree_map_type_is_mapped_over_value() { 53 | #[derive(Functor, Debug, PartialEq)] 54 | struct Test(BTreeMap<(), T>); 55 | 56 | let src = Test([((), T1)].into()); 57 | let dst = src.fmap(|_| T2); 58 | 59 | assert_eq!(dst, Test([((), T2)].into())); 60 | } 61 | 62 | // todo: not implemented 63 | // #[test] 64 | // fn field_of_btree_map_into_iter_type_is_mapped_over_key() { 65 | // #[derive(Functor, Debug)] 66 | // struct Test(btree_map::IntoIter); 67 | // 68 | // let src = Test(BTreeMap::from([(T1, ())]).into_iter()); 69 | // let dst = src.fmap(|_| T2); 70 | // 71 | // assert_eq!(dst.0.collect::>(), [(T2, ())]); 72 | // } 73 | 74 | // todo: not implemented 75 | // #[test] 76 | // fn field_of_btree_map_into_iter_type_is_mapped_over_value() { 77 | // #[derive(Functor, Debug)] 78 | // struct Test(btree_map::IntoIter<(), T>); 79 | // 80 | // let src = Test(BTreeMap::from([((), T1)]).into_iter()); 81 | // let dst = src.fmap(|_| T2); 82 | // 83 | // assert_eq!(dst.0.collect::>(), [((), T2)]); 84 | // } 85 | 86 | #[test] 87 | fn field_of_btree_set_type_is_mapped() { 88 | #[derive(Functor, Debug, PartialEq)] 89 | struct Test(BTreeSet); 90 | 91 | let src = Test([T1].into()); 92 | let dst = src.fmap(|_| T2); 93 | 94 | assert_eq!(dst, Test([T2].into())); 95 | } 96 | 97 | // todo: not implemented 98 | // #[test] 99 | // fn field_of_btree_set_into_iter_type_is_mapped() { 100 | // #[derive(Functor, Debug)] 101 | // struct Test(btree_set::IntoIter); 102 | // 103 | // let src = Test(BTreeSet::from([T1]).into_iter()); 104 | // let dst = src.fmap(|_| T2); 105 | // 106 | // assert_eq!(dst.0.collect::>(), [T2]); 107 | // } 108 | 109 | #[test] 110 | fn field_of_linked_list_type_is_mapped() { 111 | #[derive(Functor, Debug, PartialEq)] 112 | struct Test(LinkedList); 113 | 114 | let src = Test([T1].into()); 115 | let dst = src.fmap(|_| T2); 116 | 117 | assert_eq!(dst, Test([T2].into())); 118 | } 119 | 120 | // todo: not implemented 121 | // #[test] 122 | // fn field_of_linked_list_into_iter_type_is_mapped() { 123 | // #[derive(Functor, Debug)] 124 | // struct Test(linked_list::IntoIter); 125 | // 126 | // let src = Test(LinkedList::from([T1]).into_iter()); 127 | // let dst = src.fmap(|_| T2); 128 | // 129 | // assert_eq!(dst.0.collect::>(), [T2]); 130 | // } 131 | 132 | #[test] 133 | fn field_of_vec_type_is_mapped() { 134 | #[derive(Functor, Debug, PartialEq)] 135 | struct Test(Vec); 136 | 137 | let src = Test(vec![T1, T1]); 138 | let dst = src.fmap(|_| T2); 139 | 140 | assert_eq!(dst, Test(vec![T2, T2])); 141 | } 142 | 143 | // todo: not implemented 144 | // #[test] 145 | // fn field_of_vec_into_iter_type_is_mapped() { 146 | // #[derive(Functor, Debug)] 147 | // struct Test(vec::IntoIter); 148 | // 149 | // let src = Test(vec![T1, T1].into_iter()); 150 | // let dst = src.fmap(|_| T2); 151 | // 152 | // assert_eq!(dst.0.collect::>(), [T2, T2]); 153 | // } 154 | 155 | #[test] 156 | fn field_of_vec_deque_type_is_mapped() { 157 | #[derive(Functor, Debug, PartialEq)] 158 | struct Test(VecDeque); 159 | 160 | let src = Test([T1, T1].into()); 161 | let dst = src.fmap(|_| T2); 162 | 163 | assert_eq!(dst, Test([T2, T2].into())); 164 | } 165 | 166 | // todo: not implemented 167 | // #[test] 168 | // fn field_of_vec_deque_into_iter_type_is_mapped() { 169 | // #[derive(Functor, Debug)] 170 | // struct Test(vec_deque::IntoIter); 171 | // 172 | // let src = Test(VecDeque::from([T1, T1]).into_iter()); 173 | // let dst = src.fmap(|_| T2); 174 | // 175 | // assert_eq!(dst.0.collect::>(), [T2, T2]); 176 | // } 177 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/impl_core.rs: -------------------------------------------------------------------------------- 1 | use crate::funcmap::{T1, T2}; 2 | use core::marker::PhantomData; 3 | use functor_derive::Functor; 4 | use std::cell::{Cell, RefCell, UnsafeCell}; 5 | use std::ops::ControlFlow; 6 | 7 | #[test] 8 | fn field_of_array_type_is_mapped() { 9 | #[derive(Functor, Debug, PartialEq)] 10 | struct Test([T; 2]); 11 | 12 | let src = Test([T1, T1]); 13 | let dst = src.fmap(|_| T2); 14 | 15 | assert_eq!(dst, Test([T2, T2])); 16 | } 17 | 18 | // todo: not implemented 19 | // #[test] 20 | // fn field_of_bound_type_is_mapped() { 21 | // #[derive(Functor, Debug, PartialEq)] 22 | // struct Test(Bound); 23 | // 24 | // let src = Test(Bound::Included(T1)); 25 | // let dst = src.fmap(|_| T2); 26 | // 27 | // assert_eq!(dst, Test(Bound::Included(T2))); 28 | // } 29 | 30 | #[test] 31 | fn field_of_cell_type_is_mapped() { 32 | #[derive(Functor, Debug, PartialEq)] 33 | struct Test(Cell); 34 | 35 | let src = Test(Cell::new(T1)); 36 | let dst = src.fmap(|_| T2); 37 | 38 | assert_eq!(dst, Test(Cell::new(T2))); 39 | } 40 | 41 | #[test] 42 | fn field_of_control_flow_type_is_mapped_over_break() { 43 | #[derive(Functor, Debug, PartialEq)] 44 | struct Test(ControlFlow); 45 | 46 | let src = Test(ControlFlow::Break(T1)); 47 | let dst = src.fmap(|_| T2); 48 | 49 | assert_eq!(dst, Test(ControlFlow::Break(T2))); 50 | } 51 | 52 | #[test] 53 | fn field_of_control_flow_type_is_mapped_over_continue() { 54 | #[derive(Functor, Debug, PartialEq)] 55 | struct Test(ControlFlow<(), T>); 56 | 57 | let src = Test(ControlFlow::Continue(T1)); 58 | let dst = src.fmap(|_| T2); 59 | 60 | assert_eq!(dst, Test(ControlFlow::Continue(T2))); 61 | } 62 | 63 | #[test] 64 | fn field_of_option_type_is_mapped() { 65 | #[derive(Functor, Debug, PartialEq)] 66 | struct Test(Option); 67 | 68 | let src = Test(Some(T1)); 69 | let dst = src.fmap(|_| T2); 70 | 71 | assert_eq!(dst, Test(Some(T2))); 72 | } 73 | 74 | // todo: not implemented 75 | // #[test] 76 | // fn field_of_option_into_iter_type_is_mapped() { 77 | // #[derive(Functor, Debug)] 78 | // struct Test(option::IntoIter); 79 | // 80 | // let src = Test(Some(T1).into_iter()); 81 | // let dst = src.fmap(|_| T2); 82 | // 83 | // assert_eq!(dst.0.collect::>(), vec![T2]); 84 | // } 85 | 86 | #[test] 87 | fn field_of_phantom_data_type_is_mapped() { 88 | #[derive(Functor, Debug, PartialEq)] 89 | struct Test(PhantomData); 90 | 91 | let src = Test(PhantomData::); 92 | let dst = src.fmap(|_| T2); 93 | 94 | assert_eq!(dst, Test(PhantomData::)); 95 | } 96 | 97 | // todo: not implemented 98 | // #[test] 99 | // fn field_of_poll_type_is_mapped() { 100 | // #[derive(Functor, Debug, PartialEq)] 101 | // struct Test(Poll); 102 | // 103 | // let src = Test(Poll::Ready(T1)); 104 | // let dst = src.fmap(|_| T2); 105 | // 106 | // assert_eq!(dst, Test(Poll::Ready(T2))); 107 | // } 108 | 109 | // todo: not implemented 110 | // #[test] 111 | // fn field_of_range_type_is_mapped() { 112 | // #[derive(Functor, Debug, PartialEq)] 113 | // struct Test(Range); 114 | // 115 | // let src = Test(T1..T1); 116 | // let dst = src.fmap(|_| T2); 117 | // 118 | // assert_eq!(dst, Test(T2..T2)); 119 | // } 120 | 121 | // todo: not implemented 122 | // #[test] 123 | // fn field_of_range_from_type_is_mapped() { 124 | // #[derive(Functor, Debug, PartialEq)] 125 | // struct Test(RangeFrom); 126 | // 127 | // let src = Test(T1..); 128 | // let dst = src.fmap(|_| T2); 129 | // 130 | // assert_eq!(dst, Test(T2..)); 131 | // } 132 | 133 | // todo: not implemented 134 | // #[test] 135 | // fn field_of_range_inclusive_type_is_mapped() { 136 | // #[derive(Functor, Debug, PartialEq)] 137 | // struct Test(RangeInclusive); 138 | // 139 | // let src = Test(T1..=T1); 140 | // let dst = src.fmap(|_| T2); 141 | // 142 | // assert_eq!(dst, Test(T2..=T2)); 143 | // } 144 | 145 | // todo: not implemented 146 | // #[test] 147 | // fn field_of_range_to_type_is_mapped() { 148 | // #[derive(Functor, Debug, PartialEq)] 149 | // struct Test(RangeTo); 150 | // 151 | // let src = Test(..T1); 152 | // let dst = src.fmap(|_| T2); 153 | // 154 | // assert_eq!(dst, Test(..T2)); 155 | // } 156 | 157 | // todo: not implemented 158 | // #[test] 159 | // fn field_of_range_to_inclusive_type_is_mapped() { 160 | // #[derive(Functor, Debug, PartialEq)] 161 | // struct Test(RangeToInclusive); 162 | // 163 | // let src = Test(..=T1); 164 | // let dst = src.fmap(|_| T2); 165 | // 166 | // assert_eq!(dst, Test(..=T2)); 167 | // } 168 | 169 | #[test] 170 | fn field_of_ref_cell_type_is_mapped() { 171 | #[derive(Functor, Debug, PartialEq)] 172 | struct Test(RefCell); 173 | 174 | let src = Test(RefCell::new(T1)); 175 | let dst = src.fmap(|_| T2); 176 | 177 | assert_eq!(dst, Test(RefCell::new(T2))); 178 | } 179 | 180 | #[test] 181 | fn field_of_result_type_is_mapped_over_value() { 182 | #[derive(Functor, Debug, PartialEq)] 183 | struct Test(Result); 184 | 185 | let src = Test(Ok(T1)); 186 | let dst = src.fmap(|_| T2); 187 | 188 | assert_eq!(dst, Test(Ok(T2))); 189 | } 190 | 191 | #[test] 192 | fn field_of_result_type_is_mapped_over_error() { 193 | #[derive(Functor, Debug, PartialEq)] 194 | struct Test(Result<(), T>); 195 | 196 | let src = Test(Err(T1)); 197 | let dst = src.fmap(|_| T2); 198 | 199 | assert_eq!(dst, Test(Err(T2))); 200 | } 201 | 202 | // todo: not implemented 203 | // #[test] 204 | // fn field_of_result_into_iter_type_is_mapped_over_value() { 205 | // #[derive(Functor, Debug)] 206 | // struct Test(result::IntoIter); 207 | // 208 | // let src = Test(Result::<_, ()>::Ok(T1).into_iter()); 209 | // let dst = src.fmap(|_| T2); 210 | // 211 | // assert_eq!(dst.0.collect::>(), vec![T2]); 212 | // } 213 | 214 | #[test] 215 | fn field_of_unsafe_cell_type_is_mapped() { 216 | #[derive(Functor, Debug)] 217 | struct Test(UnsafeCell); 218 | 219 | let src = Test(UnsafeCell::new(T1)); 220 | let dst = src.fmap(|_| T2); 221 | 222 | assert_eq!(dst.0.into_inner(), T2); 223 | } 224 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/impl_std.rs: -------------------------------------------------------------------------------- 1 | use crate::funcmap::{T1, T2}; 2 | use functor_derive::*; 3 | use std::{ 4 | collections::{HashMap, HashSet}, 5 | hash::Hash, 6 | }; 7 | 8 | #[test] 9 | fn field_of_hash_map_type_is_mapped_over_key() { 10 | #[derive(Functor, Debug, PartialEq)] 11 | struct Test(HashMap); 12 | 13 | let src = Test([(T1, ())].into()); 14 | let dst = src.fmap(|_| T2); 15 | 16 | assert_eq!(dst, Test([(T2, ())].into())); 17 | } 18 | 19 | #[test] 20 | fn field_of_hash_map_type_is_mapped_over_value() { 21 | #[derive(Functor, Debug, PartialEq)] 22 | struct Test(HashMap<(), T>); 23 | 24 | let src = Test([((), T1)].into()); 25 | let dst = src.fmap(|_| T2); 26 | 27 | assert_eq!(dst, Test([((), T2)].into())); 28 | } 29 | 30 | // We don't have iterators in our std implementations. 31 | // #[test] 32 | // fn field_of_hash_map_into_iter_type_is_mapped_over_key() { 33 | // // #[derive(Functor, Debug)] 34 | // struct Test(hash_map::IntoIter); 35 | // 36 | // let src = Test(HashMap::from([(T1, ())]).into_iter()); 37 | // let dst = src.fmap(|_| T2); 38 | // 39 | // assert_eq!(dst.0.collect::>(), [(T2, ())]); 40 | // } 41 | // 42 | // #[test] 43 | // fn field_of_hash_map_into_iter_type_is_mapped_over_value() { 44 | // #[derive(Functor, Debug)] 45 | // struct Test(hash_map::IntoIter<(), T>); 46 | // 47 | // let src = Test(HashMap::from([((), T1)]).into_iter()); 48 | // let dst = src.fmap(|_| T2); 49 | // 50 | // assert_eq!(dst.0.collect::>(), [((), T2)]); 51 | // } 52 | 53 | #[test] 54 | fn field_of_hash_set_type_is_mapped() { 55 | #[derive(Functor, Debug, PartialEq)] 56 | struct Test(HashSet); 57 | 58 | let src = Test([T1].into()); 59 | let dst = src.fmap(|_| T2); 60 | 61 | assert_eq!(dst, Test([T2].into())); 62 | } 63 | 64 | // We don't have iterators in our std implementations. 65 | // #[test] 66 | // fn field_of_hash_set_into_iter_type_is_mapped() { 67 | // #[derive(Functor, Debug)] 68 | // struct Test(hash_set::IntoIter); 69 | // 70 | // let src = Test(HashSet::from([T1]).into_iter()); 71 | // let dst = src.fmap(|_| T2); 72 | // 73 | // assert_eq!(dst.0.collect::>(), [T2]); 74 | // } 75 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/lints.rs: -------------------------------------------------------------------------------- 1 | use functor_derive_lib::Functor; 2 | 3 | #[test] 4 | fn non_camel_case_types_lint_is_allowed_on_derived_impl() { 5 | #![deny(non_camel_case_types)] 6 | 7 | #[allow(non_camel_case_types)] 8 | #[derive(Functor)] 9 | struct Test(t); 10 | } 11 | 12 | #[test] 13 | fn unused_qualifications_lint_is_allowed_on_derived_impl() { 14 | #![deny(unused_qualifications)] 15 | 16 | #[allow(unused_qualifications)] 17 | #[derive(Functor)] 18 | struct Test(core::option::Option); 19 | } 20 | 21 | #[test] 22 | fn deprecated_lint_is_allowed_on_derived_impl() { 23 | #![deny(deprecated)] 24 | 25 | #[deprecated] 26 | #[derive(Functor)] 27 | struct Deprecated(T); 28 | 29 | #[allow(deprecated)] 30 | #[derive(Functor)] 31 | struct Test(Deprecated); 32 | } 33 | 34 | #[test] 35 | fn drop_bounds_lint_is_allowed_on_derived_impl() { 36 | #![deny(drop_bounds)] 37 | 38 | #[allow(unused)] 39 | #[allow(drop_bounds)] 40 | #[derive(Functor)] 41 | struct Test(T) 42 | where 43 | T: Drop; 44 | } 45 | 46 | #[test] 47 | fn dyn_drop_lint_is_allowed_on_derived_impl() { 48 | #![deny(dyn_drop)] 49 | 50 | #[allow(dyn_drop)] 51 | #[allow(trivial_bounds)] 52 | #[derive(Functor)] 53 | struct Test(T) 54 | where 55 | for<'a> &'a dyn Drop: Copy; 56 | } 57 | 58 | // We are pretty confident these tests will succeed - it's just too much of a hassle to implement in our test setup. 59 | // #[test] 60 | // fn clippy_disallowed_method_lint_is_allowed_on_derived_impl() { 61 | // #![deny(clippy::disallowed_method)] 62 | // 63 | // // methods `func_map` and `func_map_over` are disallowed via `clippy.toml` 64 | // #[allow(clippy::disallowed_method)] 65 | // #[derive(Functor)] 66 | // struct Test(Option); 67 | // } 68 | // 69 | // #[test] 70 | // fn clippy_disallowed_type_lint_is_allowed_on_derived_impl() { 71 | // #![deny(clippy::disallowed_type)] 72 | // 73 | // // type `Option` is disallowed via `clippy.toml` 74 | // #[allow(clippy::disallowed_type)] 75 | // #[derive(Functor)] 76 | // struct Test(Option); 77 | // } 78 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/mod.rs: -------------------------------------------------------------------------------- 1 | //! This test suite was provided by Matthias Stemmler's crate [funcmap_derive](https://crates.io/crates/funcmap_derive) under the MIT license. 2 | //! The tests are translated from tests on commit [031a1b0400abd2f4ddae748ed356a02569ea982c](https://github.com/matthias-stemmler/funcmap/tree/031a1b0400abd2f4ddae748ed356a02569ea982c/funcmap_tests/tests). 3 | //! Care was taken to change the tests minimally, and were commented if no translation was possible. 4 | 5 | //! Licensed under either of 6 | //! * Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0) 7 | //! * MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT) 8 | //! at your option. 9 | 10 | #![allow(unused)] 11 | 12 | mod decorations; 13 | mod fallible; 14 | mod hygiene; 15 | mod impl_alloc; 16 | mod impl_core; 17 | mod impl_std; 18 | mod lints; 19 | mod multi_param; 20 | mod opts_crate; 21 | mod opts_params; 22 | mod single_param; 23 | mod variants; 24 | 25 | #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord)] 26 | struct T1; 27 | 28 | #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord)] 29 | struct T2; 30 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/multi_param.rs: -------------------------------------------------------------------------------- 1 | use crate::funcmap::{T1, T2}; 2 | use functor_derive::Functor; 3 | use std::marker::PhantomData; 4 | 5 | #[test] 6 | fn struct_with_multiple_generics_is_mapped() { 7 | #[derive(Functor, Debug, PartialEq)] 8 | #[functor(S as s, T as t)] 9 | struct Test(S, i32, T); 10 | 11 | let src = Test(T1, 42, T1); 12 | let dst = src.fmap_s(|_| T2); 13 | assert_eq!(dst, Test(T2, 42, T1)); 14 | 15 | let src = Test(T1, 42, T1); 16 | let dst = src.fmap_t(|_| T2); 17 | assert_eq!(dst, Test(T1, 42, T2)); 18 | } 19 | 20 | #[test] 21 | fn struct_with_non_type_generics_is_mapped() { 22 | #[derive(Functor, Debug, PartialEq)] 23 | struct Test<'a, T, const N: usize>(T, PhantomData<&'a ()>); 24 | 25 | let src = Test::<'_, _, 42>(T1, PhantomData); 26 | let dst = src.fmap(|_| T2); 27 | 28 | assert_eq!(dst, Test::<'_, _, 42>(T2, PhantomData)); 29 | } 30 | 31 | #[test] 32 | fn struct_with_const_generics_before_type_generics_is_mapped() { 33 | #[derive(Functor, Debug, PartialEq)] 34 | #[functor(T)] 35 | struct Test(S, T); 36 | 37 | let src = Test::<42, _, 42, _>(T1, T1); 38 | let dst = src.fmap(|_| T2); 39 | 40 | assert_eq!(dst, Test(T1, T2)); 41 | } 42 | 43 | #[test] 44 | fn field_of_generic_type_is_mapped() { 45 | #[derive(Functor, Debug, PartialEq)] 46 | struct Inner<'a, S, T, const N: usize>(S, T, PhantomData<&'a ()>); 47 | 48 | #[derive(Functor, Debug, PartialEq)] 49 | #[functor(S as s, T as t)] 50 | struct Test<'a, S, T, const N: usize>(Inner<'a, S, T, N>); 51 | 52 | let src = Test::<'_, _, _, 42>(Inner(T1, T1, PhantomData)); 53 | let dst = src.fmap_s(|_| T2); 54 | assert_eq!(dst, Test::<'_, _, _, 42>(Inner(T2, T1, PhantomData))); 55 | 56 | let src = Test::<'_, _, _, 42>(Inner(T1, T1, PhantomData)); 57 | let dst = src.fmap_t(|_| T2); 58 | assert_eq!(dst, Test::<'_, _, _, 42>(Inner(T1, T2, PhantomData))); 59 | } 60 | 61 | #[test] 62 | fn field_of_repeated_generic_type_is_mapped() { 63 | #[derive(Functor, Debug, PartialEq)] 64 | struct Inner<'a, S, T, const N: usize>(S, T, PhantomData<&'a ()>); 65 | 66 | #[derive(Functor, Debug, PartialEq)] 67 | struct Test<'a, T, const N: usize>(Inner<'a, T, T, N>); 68 | 69 | let src = Test::<'_, _, 42>(Inner(T1, T1, PhantomData)); 70 | let dst = src.fmap(|_| T2); 71 | 72 | assert_eq!(dst, Test::<'_, _, 42>(Inner(T2, T2, PhantomData))); 73 | } 74 | 75 | #[test] 76 | fn field_of_generic_type_with_const_literal_before_generic_type_is_mapped() { 77 | #[derive(Functor, Debug, PartialEq)] 78 | struct Inner(S, T); 79 | 80 | #[derive(Functor, Debug, PartialEq)] 81 | struct Test(Inner<42, T, 42, T>); 82 | 83 | let src = Test(Inner(T1, T1)); 84 | let dst = src.fmap(|_| T2); 85 | 86 | assert_eq!(dst, Test(Inner(T2, T2))); 87 | } 88 | 89 | #[test] 90 | fn field_of_generic_type_with_const_alias_before_generic_type_is_mapped() { 91 | #[derive(Functor, Debug, PartialEq)] 92 | struct Inner(S, T); 93 | 94 | const N: usize = 42; 95 | 96 | // here the derive macro cannot know whether `N` is a type or a const 97 | #[derive(Functor, Debug, PartialEq)] 98 | struct Test(Inner); 99 | 100 | let src = Test(Inner(T1, T1)); 101 | let dst = src.fmap(|_| T2); 102 | 103 | assert_eq!(dst, Test(Inner(T2, T2))); 104 | } 105 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/opts_crate.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn crate_path_uses_leading_colon_by_default() { 3 | use ::functor_derive::Functor; 4 | 5 | #[derive(Functor)] 6 | struct Test(T); 7 | 8 | // would be referred to if derive macro produced `funcmap::..` instead of `::funcmap::..` 9 | mod functor_derive {} 10 | } 11 | 12 | // Our macro invocation does not support renaming the crate path. 13 | // #[test] 14 | // fn crate_path_can_be_configured() { 15 | // use fake_funcmap::FuncMap; 16 | // 17 | // #[derive(FuncMap)] 18 | // #[funcmap(crate = "fake_funcmap")] 19 | // struct Test(T); 20 | // 21 | // // would be conflicting if `Test` implemented `funcmap::FuncMap` 22 | // impl AssertNotOriginalFuncMap for Test {} 23 | // 24 | // fake_funcmap::assert::, T1, T2, fake_funcmap::TypeParam<0>>(); 25 | // } 26 | // 27 | // mod fake_funcmap { 28 | // pub use funcmap::*; 29 | // 30 | // pub struct TypeParam; 31 | // 32 | // pub trait FuncMap> { 33 | // type Output; 34 | // 35 | // fn func_map(self, _: F) -> Self::Output 36 | // where 37 | // F: FnMut(A) -> B; 38 | // } 39 | // 40 | // pub fn assert() 41 | // where 42 | // T: FuncMap, 43 | // { 44 | // } 45 | // } 46 | // 47 | // trait AssertNotOriginalFuncMap {} 48 | // 49 | // impl AssertNotOriginalFuncMap for T where T: funcmap::FuncMap {} 50 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/opts_params.rs: -------------------------------------------------------------------------------- 1 | use crate::funcmap::{T1, T2}; 2 | use functor_derive_lib::Functor; 3 | 4 | #[test] 5 | fn generics_to_be_mapped_can_be_configured() { 6 | fn noop() {} 7 | 8 | #[derive(Functor, Debug, PartialEq)] 9 | #[functor(S as s, U as u)] 10 | struct Test { 11 | value1: S, 12 | not_mappable: fn() -> T, 13 | value2: U, 14 | } 15 | 16 | let src = Test { 17 | value1: T1, 18 | not_mappable: noop, 19 | value2: T1, 20 | }; 21 | let dst = src.fmap_s(|_| T2).fmap_u(|_| T2); 22 | 23 | assert_eq!( 24 | dst, 25 | Test { 26 | value1: T2, 27 | not_mappable: noop, 28 | value2: T2, 29 | } 30 | ); 31 | } 32 | 33 | // Our macro invocation does *not* allow trailing commas. 34 | // #[test] 35 | // fn opts_accept_trailing_comma() { 36 | // #[derive(Functor)] 37 | // #[functor(S as s, T as t)] 38 | // struct Test(S, T); 39 | // } 40 | // 41 | // #[test] 42 | // fn params_opt_accepts_trailing_comma() { 43 | // #[derive(Functor)] 44 | // #[functor(S as s, T as t,)] 45 | // struct Test(S, T); 46 | // } 47 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/single_param.rs: -------------------------------------------------------------------------------- 1 | use crate::funcmap::{T1, T2}; 2 | use functor_derive::Functor; 3 | 4 | #[test] 5 | fn field_of_generic_param_type_is_mapped() { 6 | #[derive(Functor, Debug, PartialEq)] 7 | struct Test(T); 8 | 9 | let src = Test(T1); 10 | let dst = src.fmap(|_| T2); 11 | 12 | assert_eq!(dst, Test(T2)); 13 | } 14 | 15 | #[test] 16 | fn field_of_generic_type_is_mapped() { 17 | #[derive(Functor, Debug, PartialEq)] 18 | struct Inner(T); 19 | 20 | #[derive(Functor, Debug, PartialEq)] 21 | struct Test(Inner); 22 | 23 | let src = Test(Inner(T1)); 24 | let dst = src.fmap(|_| T2); 25 | 26 | assert_eq!(dst, Test(Inner(T2))); 27 | } 28 | 29 | #[test] 30 | fn field_of_nested_generic_type_is_mapped() { 31 | #[derive(Functor, Debug, PartialEq)] 32 | struct Inner(T); 33 | 34 | #[derive(Functor, Debug, PartialEq)] 35 | struct Test(Inner>); 36 | 37 | let src = Test(Inner(Inner(T1))); 38 | let dst = src.fmap(|_| T2); 39 | 40 | assert_eq!(dst, Test(Inner(Inner(T2)))); 41 | } 42 | 43 | #[test] 44 | fn field_of_non_generic_type_is_not_mapped() { 45 | #[derive(Functor, Debug, PartialEq)] 46 | struct Test(T, i32); 47 | 48 | let src = Test(T1, 42); 49 | let dst = src.fmap(|_| T2); 50 | 51 | assert_eq!(dst, Test(T2, 42)); 52 | } 53 | 54 | #[test] 55 | fn parenthesized_generic_param_is_mapped() { 56 | #[allow(unused_parens)] 57 | #[derive(Functor, Debug, PartialEq)] 58 | struct Test((T)); 59 | 60 | let src = Test(T1); 61 | let dst = src.fmap(|_| T2); 62 | 63 | assert_eq!(dst, Test(T2)); 64 | } 65 | 66 | #[test] 67 | fn tuple_entry_of_generic_param_type_is_mapped() { 68 | #[derive(Functor, Debug, PartialEq)] 69 | struct Test((T, i32, T)); 70 | 71 | let src = Test((T1, 42, T1)); 72 | let dst = src.fmap(|_| T2); 73 | 74 | assert_eq!(dst, Test((T2, 42, T2))); 75 | } 76 | -------------------------------------------------------------------------------- /functor_derive/tests/funcmap/variants.rs: -------------------------------------------------------------------------------- 1 | use crate::funcmap::{T1, T2}; 2 | use functor_derive::Functor; 3 | 4 | #[test] 5 | fn tuple_struct_is_mapped() { 6 | #[derive(Functor, Debug, PartialEq)] 7 | struct Test(T, i32, T); 8 | 9 | let src = Test(T1, 42, T1); 10 | let dst = src.fmap(|_| T2); 11 | 12 | assert_eq!(dst, Test(T2, 42, T2)); 13 | } 14 | 15 | #[test] 16 | fn struct_with_named_fields_is_mapped() { 17 | #[derive(Functor, Debug, PartialEq)] 18 | struct Test { 19 | value0: T, 20 | value1: i32, 21 | value2: T, 22 | } 23 | 24 | let src = Test { 25 | value0: T1, 26 | value1: 42, 27 | value2: T1, 28 | }; 29 | let dst = src.fmap(|_| T2); 30 | 31 | assert_eq!( 32 | dst, 33 | Test { 34 | value0: T2, 35 | value1: 42, 36 | value2: T2 37 | } 38 | ); 39 | } 40 | 41 | #[test] 42 | fn enum_unit_variant_is_mapped() { 43 | #[derive(Functor, Debug, PartialEq)] 44 | enum Test { 45 | Unit, 46 | Tuple(T, i32, T), 47 | Named { 48 | value_0: T, 49 | value_1: i32, 50 | value_2: T, 51 | }, 52 | } 53 | 54 | let src: Test = Test::Unit; 55 | let dst = src.fmap(|_| T2); 56 | 57 | assert_eq!(dst, Test::Unit); 58 | } 59 | 60 | #[test] 61 | fn enum_tuple_variant_is_mapped() { 62 | #[derive(Functor, Debug, PartialEq)] 63 | enum Test { 64 | Unit, 65 | Tuple(T, i32, T), 66 | Named { 67 | value_0: T, 68 | value_1: i32, 69 | value_2: T, 70 | }, 71 | } 72 | 73 | let src = Test::Tuple(T1, 42, T1); 74 | let dst = src.fmap(|_| T2); 75 | 76 | assert_eq!(dst, Test::Tuple(T2, 42, T2)); 77 | } 78 | 79 | #[test] 80 | fn enum_variant_with_named_fields_is_mapped() { 81 | #[derive(Functor, Debug, PartialEq)] 82 | enum Test { 83 | Unit, 84 | Tuple(T, i32, T), 85 | Named { 86 | value_0: T, 87 | value_1: i32, 88 | value_2: T, 89 | }, 90 | } 91 | 92 | let src = Test::Named { 93 | value_0: T1, 94 | value_1: 42, 95 | value_2: T1, 96 | }; 97 | let dst = src.fmap(|_| T2); 98 | 99 | assert_eq!( 100 | dst, 101 | Test::Named { 102 | value_0: T2, 103 | value_1: 42, 104 | value_2: T2 105 | } 106 | ); 107 | } 108 | -------------------------------------------------------------------------------- /functor_derive/tests/recursion.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | 3 | use functor_derive::Functor; 4 | use std::any::{Any, TypeId}; 5 | 6 | #[test] 7 | fn mutual_recursion() { 8 | #[derive(Functor)] 9 | struct TypeA { 10 | b: Option>>, 11 | v: T, 12 | } 13 | 14 | #[derive(Functor)] 15 | struct TypeB { 16 | a: Option>>, 17 | } 18 | 19 | let x = TypeA { 20 | b: Some(Box::new(TypeB { 21 | a: Some(Box::new(TypeA { 22 | b: None, 23 | v: 42usize, 24 | })), 25 | })), 26 | v: 42usize, 27 | }; 28 | 29 | assert_eq!(x.fmap(|x| x as u64).type_id(), TypeId::of::>()); 30 | } 31 | 32 | #[test] 33 | fn single_recursion() { 34 | #[derive(Functor)] 35 | struct TypeA { 36 | b: Option>>, 37 | v: T, 38 | } 39 | 40 | let x = TypeA { 41 | b: Some(Box::new(TypeA { 42 | b: Some(Box::new(TypeA { 43 | b: None, 44 | v: 42usize, 45 | })), 46 | v: 42usize, 47 | })), 48 | v: 42usize, 49 | }; 50 | 51 | assert_eq!(x.fmap(|x| x as u64).type_id(), TypeId::of::>()); 52 | } 53 | 54 | #[test] 55 | fn recursion_swap() { 56 | #[derive(Functor)] 57 | struct TypeA { 58 | b: Option>>, 59 | v1: S, 60 | v2: T, 61 | } 62 | 63 | let x = TypeA { 64 | b: Some(Box::new(TypeA { 65 | b: None, 66 | v1: 40usize, 67 | v2: 41usize, 68 | })), 69 | v1: 42usize, 70 | v2: 43usize, 71 | }; 72 | 73 | assert_eq!( 74 | x.fmap(|x| x as u64).type_id(), 75 | TypeId::of::>() 76 | ); 77 | } 78 | 79 | #[test] 80 | fn recursion_swap_mutual() { 81 | #[derive(Functor)] 82 | struct TypeA { 83 | b: Option>>, 84 | v1: S, 85 | v2: T, 86 | } 87 | 88 | #[derive(Functor)] 89 | struct TypeB { 90 | b: Option>>, 91 | v1: S, 92 | v2: T, 93 | } 94 | 95 | let x = TypeA { 96 | b: Some(Box::new(TypeB { 97 | b: None, 98 | v1: 40usize, 99 | v2: 41usize, 100 | })), 101 | v1: 42usize, 102 | v2: 43usize, 103 | }; 104 | 105 | assert_eq!( 106 | x.fmap(|x| x as u64).type_id(), 107 | TypeId::of::>() 108 | ); 109 | } 110 | -------------------------------------------------------------------------------- /functor_derive/tests/simple.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | 3 | use functor_derive::Functor; 4 | use std::any::{Any, TypeId}; 5 | use std::collections::{BTreeMap, HashMap}; 6 | 7 | // recursion, 8 | // mutual recursion, 9 | 10 | #[test] 11 | fn struct_simple() { 12 | #[derive(Functor)] 13 | struct StructSimple { 14 | field_1: A, 15 | field_2: u32, 16 | } 17 | 18 | let x = StructSimple:: { 19 | field_1: 42, 20 | field_2: 13, 21 | }; 22 | 23 | assert_eq!( 24 | x.fmap(|x| x as u64).type_id(), 25 | TypeId::of::>() 26 | ); 27 | } 28 | 29 | #[test] 30 | fn struct_chained() { 31 | #[derive(Functor)] 32 | struct StructChained { 33 | field_1: Option, 34 | field_2: Option, 35 | field_3: u32, 36 | } 37 | 38 | let x = StructChained:: { 39 | field_1: Some(42), 40 | field_2: None, 41 | field_3: 13, 42 | }; 43 | 44 | assert_eq!( 45 | x.fmap(|x| x as u64).type_id(), 46 | TypeId::of::>() 47 | ); 48 | } 49 | 50 | #[test] 51 | fn struct_tuple() { 52 | #[derive(Functor)] 53 | struct StructTuple { 54 | field_1: (A, u8, A), 55 | field_2: u32, 56 | } 57 | 58 | let x = StructTuple:: { 59 | field_1: (3, 5, 8), 60 | field_2: 13, 61 | }; 62 | 63 | assert_eq!( 64 | x.fmap(|x| x as u64).type_id(), 65 | TypeId::of::>() 66 | ); 67 | } 68 | 69 | #[test] 70 | fn struct_hashmap() { 71 | #[derive(Functor)] 72 | struct StructHashMap { 73 | field_1: A, 74 | field_2: HashMap, 75 | } 76 | 77 | let x = StructHashMap:: { 78 | field_1: 42, 79 | field_2: HashMap::from([(13, 255)]), 80 | }; 81 | 82 | assert_eq!( 83 | x.fmap(|x| x as u64).type_id(), 84 | TypeId::of::>() 85 | ); 86 | } 87 | 88 | #[test] 89 | fn struct_btreemap() { 90 | #[derive(Functor)] 91 | struct StructBTreeMap { 92 | field_1: A, 93 | field_2: BTreeMap, 94 | } 95 | 96 | let x = StructBTreeMap:: { 97 | field_1: 42, 98 | field_2: BTreeMap::from([(13, 255)]), 99 | }; 100 | 101 | assert_eq!( 102 | x.fmap(|x| x as u64).type_id(), 103 | TypeId::of::>() 104 | ); 105 | } 106 | 107 | #[test] 108 | fn struct_array() { 109 | #[derive(Functor)] 110 | struct StructArray { 111 | field_1: [A; 3], 112 | field_2: u8, 113 | } 114 | 115 | let x = StructArray:: { 116 | field_1: [1, 2, 3], 117 | field_2: 42, 118 | }; 119 | 120 | assert_eq!( 121 | x.fmap(|x| x as u64).type_id(), 122 | TypeId::of::>() 123 | ); 124 | } 125 | 126 | #[test] 127 | fn struct_paren() { 128 | #[derive(Functor)] 129 | struct StructParen { 130 | field_1: (A), 131 | field_2: u8, 132 | } 133 | 134 | let x = StructParen:: { 135 | field_1: 1, 136 | field_2: 42, 137 | }; 138 | 139 | assert_eq!( 140 | x.fmap(|x| x as u64).type_id(), 141 | TypeId::of::>() 142 | ); 143 | } 144 | 145 | #[test] 146 | fn enum_tuple() { 147 | #[derive(Functor)] 148 | enum EnumTuple { 149 | Var1(A), 150 | Var2(bool), 151 | Var3, 152 | } 153 | 154 | let x = EnumTuple::Var1(18usize); 155 | 156 | assert_eq!( 157 | x.fmap(|x| x as u64).type_id(), 158 | TypeId::of::>() 159 | ); 160 | } 161 | 162 | #[test] 163 | fn enum_struct() { 164 | #[derive(Functor)] 165 | enum EnumStruct { 166 | Var1 { x: A }, 167 | Var2 { y: bool }, 168 | Var3, 169 | } 170 | 171 | let x = EnumStruct::Var1 { x: 18usize }; 172 | 173 | assert_eq!( 174 | x.fmap(|x| x as u64).type_id(), 175 | TypeId::of::>() 176 | ); 177 | } 178 | 179 | #[test] 180 | fn enum_mixed() { 181 | #[derive(Functor)] 182 | enum EnumMixed { 183 | Var1 { x: A }, 184 | Var2(bool), 185 | Var3, 186 | } 187 | 188 | let x = EnumMixed::Var1 { x: 18usize }; 189 | 190 | assert_eq!( 191 | x.fmap(|x| x as u64).type_id(), 192 | TypeId::of::>() 193 | ); 194 | } 195 | 196 | #[test] 197 | fn generic_overload() { 198 | #[derive(Functor)] 199 | struct StructLifeTimes<'a, 'b, const N: usize, A, T> { 200 | field_1: A, 201 | field_2: &'a u32, 202 | field_3: &'b bool, 203 | field_4: T, 204 | field_5: [A; N], 205 | } 206 | 207 | let x = StructLifeTimes::<2, usize, bool> { 208 | field_1: 42, 209 | field_2: &13, 210 | field_3: &false, 211 | field_4: true, 212 | field_5: [1, 2], 213 | }; 214 | 215 | assert_eq!( 216 | x.fmap(|x| x as u64).type_id(), 217 | TypeId::of::>() 218 | ); 219 | } 220 | 221 | #[test] 222 | fn indirect_generic() { 223 | #[derive(Functor)] 224 | struct IndirectGeneric { 225 | field_1: Option>, 226 | } 227 | 228 | let x = IndirectGeneric { 229 | field_1: Some(Some(18usize)), 230 | }; 231 | 232 | assert_eq!( 233 | x.fmap(|x| x as u64).type_id(), 234 | TypeId::of::>() 235 | ); 236 | } 237 | 238 | #[test] 239 | fn indirect_tuple_generic() { 240 | #[derive(Functor)] 241 | struct IndirectTupleGeneric { 242 | field_1: Vec, Vec>, usize)>>, 243 | } 244 | 245 | let x = IndirectTupleGeneric:: { 246 | field_1: vec![vec![(18, vec![18], vec![vec![42]], 9)]], 247 | }; 248 | 249 | assert_eq!( 250 | x.fmap(|x| x as u64).type_id(), 251 | TypeId::of::>() 252 | ); 253 | } 254 | 255 | #[test] 256 | fn chained_fmap() { 257 | #[derive(Functor)] 258 | struct StructSimple { 259 | field_1: ApplePie, 260 | } 261 | 262 | // This incredible struct name was brought to you by Jonathan :)! 263 | #[derive(Functor)] 264 | struct ApplePie { 265 | apple: A, 266 | pie: B, 267 | } 268 | 269 | let x = StructSimple:: { 270 | field_1: ApplePie { apple: 15, pie: 17 }, 271 | }; 272 | 273 | assert_eq!( 274 | x.fmap(|x| x as u64).type_id(), 275 | TypeId::of::>() 276 | ); 277 | } 278 | 279 | #[test] 280 | fn explicit_path() { 281 | #[allow(unused)] 282 | #[derive(Functor)] 283 | struct Test(core::option::Option); 284 | } 285 | -------------------------------------------------------------------------------- /functor_derive/tests/std.rs: -------------------------------------------------------------------------------- 1 | use functor_derive::impl_std::{FunctorHashKeys, FunctorHashSet}; 2 | use functor_derive::{Functor, FunctorValues}; 3 | use std::collections::{HashMap, HashSet}; 4 | 5 | fn map(value: usize) -> u64 { 6 | value as u64 7 | } 8 | 9 | #[test] 10 | fn option() { 11 | let x = Some(42usize); 12 | let y = None::; 13 | 14 | assert_eq!(x.fmap(map), Some(42u64)); 15 | assert_eq!(y.fmap(map), None::); 16 | } 17 | 18 | #[test] 19 | fn result_ok() { 20 | let x = Result::::Ok(42usize); 21 | let y = Result::::Err(13usize); 22 | 23 | assert_eq!(x.fmap(map), Ok(42u64)); 24 | assert_eq!(y.fmap(map), Err(13usize)); 25 | } 26 | 27 | #[test] 28 | fn vec() { 29 | let x = vec![42usize, 13usize]; 30 | 31 | assert_eq!(x.fmap(map), vec![42u64, 13u64]); 32 | } 33 | 34 | #[test] 35 | fn hashmap_keys() { 36 | let x = HashMap::from([(42usize, 13usize)]); 37 | 38 | assert_eq!(x.fmap_keys(map), HashMap::from([(42u64, 13usize)])); 39 | } 40 | 41 | #[test] 42 | fn hashmap_values() { 43 | let x = HashMap::from([(42usize, 13usize)]); 44 | 45 | assert_eq!(x.fmap_values(map), HashMap::from([(42usize, 13u64)])); 46 | } 47 | 48 | #[test] 49 | fn hashset() { 50 | let x = HashSet::from([42usize, 13usize]); 51 | 52 | assert_eq!(x.fmap(map), HashSet::from([42u64, 13u64])); 53 | } 54 | -------------------------------------------------------------------------------- /functor_derive/tests/try.rs: -------------------------------------------------------------------------------- 1 | use functor_derive::Functor; 2 | use std::any::{Any, TypeId}; 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | 5 | fn test_try(v: usize) -> Result { 6 | Ok(v as u64) 7 | } 8 | 9 | #[test] 10 | fn try_struct_simple() { 11 | #[derive(Functor)] 12 | struct StructSimple { 13 | field_1: A, 14 | field_2: u32, 15 | } 16 | 17 | let x = StructSimple:: { 18 | field_1: 42, 19 | field_2: 13, 20 | }; 21 | 22 | assert_eq!( 23 | x.try_fmap(test_try).type_id(), 24 | TypeId::of::, ()>>() 25 | ); 26 | } 27 | 28 | #[test] 29 | fn try_recursive() { 30 | #[derive(Functor)] 31 | struct StructSimple { 32 | field_1: Option>>, 33 | field_2: A, 34 | } 35 | 36 | let x = StructSimple:: { 37 | field_1: Some(Box::new(StructSimple { 38 | field_1: None, 39 | field_2: 15, 40 | })), 41 | field_2: 13, 42 | }; 43 | 44 | assert_eq!( 45 | x.try_fmap(test_try).type_id(), 46 | TypeId::of::, ()>>() 47 | ); 48 | } 49 | 50 | #[test] 51 | fn try_array() { 52 | let x = ["1".to_string(), "2".to_string(), "3".to_string()]; 53 | let v = x.try_fmap(|x| x.parse::()); 54 | 55 | assert_eq!(v, Ok([1, 2, 3])); 56 | } 57 | 58 | #[test] 59 | fn try_array_fail() { 60 | let x = ["1".to_string(), "two".to_string(), "3".to_string()]; 61 | let v = x.try_fmap(|x| x.parse::()); 62 | 63 | assert!(v.is_err()); 64 | } 65 | 66 | #[test] 67 | fn try_array_drops() { 68 | struct DropCheck<'a> { 69 | dropped: &'a AtomicBool, 70 | should_fail: bool, 71 | } 72 | 73 | impl Drop for DropCheck<'_> { 74 | fn drop(&mut self) { 75 | self.dropped.store(true, Ordering::Relaxed); 76 | } 77 | } 78 | 79 | let dropped_1 = AtomicBool::new(false); 80 | let dropped_2 = AtomicBool::new(false); 81 | 82 | let x = [ 83 | DropCheck { 84 | dropped: &dropped_1, 85 | should_fail: false, 86 | }, 87 | DropCheck { 88 | dropped: &dropped_2, 89 | should_fail: true, 90 | }, 91 | ]; 92 | 93 | let drop_check_array = x.try_fmap(|drop_check| { 94 | if drop_check.should_fail { 95 | Err(()) 96 | } else { 97 | Ok(drop_check) 98 | } 99 | }); 100 | 101 | assert!(drop_check_array.is_err()); 102 | // Dropped because it's already initialized 103 | assert!(dropped_1.load(Ordering::Relaxed)); 104 | // Dropped by the iterator 105 | assert!(dropped_2.load(Ordering::Relaxed)); 106 | } 107 | -------------------------------------------------------------------------------- /functor_derive_lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "functor_derive_lib" 3 | version = "0.4.3" 4 | authors = ["Jonathan Brouwer ", "Julia Dijkstra"] 5 | description = "The proc macro for a derive macro to derive a functor for a type." 6 | keywords = ["proc-macro", "derive", "functor"] 7 | edition = "2021" 8 | license = "MIT" 9 | repository = "https://github.com/binary-banter/functor_derive" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | quote = "1.0.33" 16 | proc-macro2 = "1.0.67" 17 | syn = { version = "2.0.33", features = ["full"] } 18 | proc-macro-error = "1.0.4" 19 | itertools = "0.12.0" 20 | -------------------------------------------------------------------------------- /functor_derive_lib/README.md: -------------------------------------------------------------------------------- 1 | Accompanies the `functor_derive` crate [here](https://crates.io/crates/functor_derive). 2 | -------------------------------------------------------------------------------- /functor_derive_lib/src/generate_fmap_body.rs: -------------------------------------------------------------------------------- 1 | use crate::generate_map::generate_map_from_type; 2 | use proc_macro2::{Ident, TokenStream}; 3 | use proc_macro_error::abort_call_site; 4 | use quote::{format_ident, quote}; 5 | use syn::{Data, DataEnum, DataStruct, Fields, Index}; 6 | 7 | pub fn generate_fmap_body( 8 | data: &Data, 9 | def_name: &Ident, 10 | functor_param: &Ident, 11 | is_try: bool, 12 | ) -> Option { 13 | match data { 14 | Data::Struct(strct) => generate_fmap_body_struct(strct, functor_param, def_name, is_try), 15 | Data::Enum(enm) => generate_fmap_body_enum(enm, functor_param, def_name, is_try), 16 | Data::Union(_) => abort_call_site!("Deriving Functor on unions is unsupported."), 17 | } 18 | } 19 | 20 | fn generate_fmap_body_enum( 21 | enm: &DataEnum, 22 | functor_param: &Ident, 23 | def_name: &Ident, 24 | is_try: bool, 25 | ) -> Option { 26 | let variants = enm.variants.iter().map(|variant| { 27 | let variant_name = &variant.ident; 28 | let field = match &variant.fields { 29 | Fields::Named(fields) => { 30 | let names = fields 31 | .named 32 | .iter() 33 | .map(|field| field.ident.as_ref().unwrap()); 34 | let fields = fields.named.iter().map(|field| { 35 | let field_name = field.ident.as_ref().unwrap(); 36 | let field = generate_map_from_type( 37 | &field.ty, 38 | functor_param, 39 | "e!(#field_name), 40 | is_try, 41 | )? 42 | .0; 43 | Some(quote!(#field_name: #field)) 44 | }).collect::>>()?; 45 | 46 | quote!(Self::#variant_name { #(#names),* } => { 47 | #def_name::#variant_name { 48 | #(#fields),* 49 | } 50 | }) 51 | } 52 | Fields::Unnamed(fields) => { 53 | let names = (0..) 54 | .map(|i| format_ident!("v{i}")) 55 | .take(fields.unnamed.len()); 56 | let fields = fields.unnamed.iter().zip(names.clone()).map(|(field, i)| { 57 | generate_map_from_type(&field.ty, functor_param, "e!(#i), is_try).map(|(v, _)| v) 58 | }).collect::>>()?; 59 | quote!(Self::#variant_name(#(#names),*) => #def_name::#variant_name(#(#fields),*)) 60 | } 61 | Fields::Unit => quote!(Self::#variant_name => #def_name::#variant_name), 62 | }; 63 | Some(field) 64 | }).collect::>>()?; 65 | Some(quote!(match self {#(#variants),*})) 66 | } 67 | 68 | fn generate_fmap_body_struct( 69 | strct: &DataStruct, 70 | functor_param: &Ident, 71 | def_name: &Ident, 72 | is_try: bool, 73 | ) -> Option { 74 | match &strct.fields { 75 | Fields::Named(fields) => { 76 | let fields = fields 77 | .named 78 | .iter() 79 | .map(|field| { 80 | let field_name = field.ident.as_ref().unwrap(); 81 | let field = generate_map_from_type( 82 | &field.ty, 83 | functor_param, 84 | "e!(self.#field_name), 85 | is_try, 86 | )? 87 | .0; 88 | Some(quote!(#field_name: #field)) 89 | }) 90 | .collect::>>()?; 91 | Some(quote!(#def_name{#(#fields),*})) 92 | } 93 | Fields::Unnamed(s) => { 94 | let fields = s 95 | .unnamed 96 | .iter() 97 | .enumerate() 98 | .map(|(i, field)| { 99 | let i = Index::from(i); 100 | Some( 101 | generate_map_from_type(&field.ty, functor_param, "e!(self.#i), is_try)? 102 | .0, 103 | ) 104 | }) 105 | .collect::>>()?; 106 | Some(quote!(#def_name(#(#fields),*))) 107 | } 108 | Fields::Unit => abort_call_site!("Cannot derive `Functor` for Unit Structs."), 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /functor_derive_lib/src/generate_map.rs: -------------------------------------------------------------------------------- 1 | use itertools::Itertools; 2 | use proc_macro2::{Ident, TokenStream}; 3 | use proc_macro_error::abort_call_site; 4 | use quote::{format_ident, quote}; 5 | use syn::{GenericArgument, Index, PathArguments, ReturnType, Type, TypeParamBound, TypePath}; 6 | 7 | pub fn generate_map_from_type( 8 | typ: &Type, 9 | param: &Ident, 10 | field: &TokenStream, 11 | is_try: bool, 12 | ) -> Option<(TokenStream, bool)> { 13 | let stream = match typ { 14 | Type::Path(path) => return generate_map_from_path(path, param, field, is_try), 15 | Type::Tuple(tuple) => { 16 | let positions = tuple 17 | .elems 18 | .iter() 19 | .enumerate() 20 | .map(|(i, x)| { 21 | let i = Index::from(i); 22 | let field = generate_map_from_type(x, param, "e!(#field.#i), is_try)?.0; 23 | Some(quote!(#field,)) 24 | }) 25 | .collect::>>()?; 26 | quote!((#(#positions)*)) 27 | } 28 | Type::Array(array) => { 29 | if type_contains_param(typ, param) { 30 | let map = generate_map_from_type(&array.elem, param, "e!(__v), is_try)?.0; 31 | if is_try { 32 | quote!(#field.try_fmap(|__v| Ok(#map))?) 33 | } else { 34 | quote!(#field.map(|__v| #map)) 35 | } 36 | } else { 37 | quote!(#field) 38 | } 39 | } 40 | Type::Paren(p) => generate_map_from_type(&p.elem, param, field, is_try)?.0, 41 | // We cannot possibly map these, but passing them through is fine. 42 | Type::BareFn(_) 43 | | Type::Reference(_) 44 | | Type::Ptr(_) 45 | | Type::Slice(_) 46 | | Type::Never(_) 47 | | Type::Macro(_) 48 | | Type::Infer(_) 49 | | Type::ImplTrait(_) 50 | | Type::TraitObject(_) 51 | | Type::Verbatim(_) 52 | | Type::Group(_) => { 53 | if type_contains_param(typ, param) { 54 | return None; 55 | } else { 56 | quote!(#field) 57 | } 58 | } 59 | _ => panic!("Found unknown type"), 60 | }; 61 | 62 | Some((stream, false)) 63 | } 64 | 65 | fn generate_map_from_path( 66 | path: &TypePath, 67 | param: &Ident, 68 | field: &TokenStream, 69 | is_try: bool, 70 | ) -> Option<(TokenStream, bool)> { 71 | // Simply return the field if it does not contain the parameter `param`. 72 | if !type_contains_param(&Type::Path(path.clone()), param) { 73 | return Some((quote!(#field), false)); 74 | } 75 | 76 | // If the path consists of exactly one segment, then it must be the param. 77 | match path.path.segments.iter().exactly_one() { 78 | Ok(segment) if &segment.ident == param => { 79 | if is_try { 80 | return Some((quote!(__f(#field)?), true)); 81 | } else { 82 | return Some((quote!(__f(#field)), true)); 83 | } 84 | } 85 | _ => {} 86 | } 87 | 88 | let Some(last_segment) = path.path.segments.last() else { 89 | unreachable!() 90 | }; 91 | 92 | let PathArguments::AngleBracketed(args) = &last_segment.arguments else { 93 | unreachable!() 94 | }; 95 | 96 | let mut tokens = quote!(#field); 97 | 98 | let enumerated_type_params = args 99 | .args 100 | .iter() 101 | .enumerate() 102 | .filter_map(|(idx, arg)| { 103 | if let GenericArgument::Type(typ) = arg { 104 | Some((idx, typ)) 105 | } else { 106 | None 107 | } 108 | }) 109 | .filter(|(_, typ)| type_contains_param(typ, param)); 110 | 111 | // Loop over all arguments that contain `param` 112 | for (type_arg_idx, type_arg) in enumerated_type_params { 113 | let (map, is_end) = generate_map_from_type(type_arg, param, "e!(v), is_try)?; 114 | 115 | if is_try { 116 | let map_ident = format_ident!("__try_fmap_{type_arg_idx}_ref"); 117 | if is_end { 118 | tokens.extend(quote!(.#map_ident(__f)?)) 119 | } else { 120 | tokens.extend(quote!(.#map_ident(&|v| { Ok(#map) })?)) 121 | } 122 | } else { 123 | let map_ident = format_ident!("__fmap_{type_arg_idx}_ref"); 124 | if is_end { 125 | tokens.extend(quote!(.#map_ident(__f))); 126 | } else { 127 | tokens.extend(quote!(.#map_ident(&|v| { #map }))); 128 | } 129 | } 130 | } 131 | 132 | Some((tokens, false)) 133 | } 134 | 135 | /// Returns whether or not the given type `typ` contains the parameter `param`. 136 | fn type_contains_param(typ: &Type, param: &Ident) -> bool { 137 | match typ { 138 | Type::Path(path) => { 139 | // If the path consists of exactly one segment, then it must be the param. 140 | match path.path.segments.iter().exactly_one() { 141 | Ok(segment) if &segment.ident == param => return true, 142 | _ => {} 143 | } 144 | 145 | // If the path is empty, something weird happened. 146 | let Some(last_segment) = path.path.segments.last() else { 147 | return false; 148 | }; 149 | 150 | let PathArguments::AngleBracketed(bracketed_params) = &last_segment.arguments else { 151 | return false; 152 | }; 153 | 154 | bracketed_params.args.iter().any(|bracketed_param| { 155 | matches!(bracketed_param, GenericArgument::Type(typ) if type_contains_param(typ, param)) 156 | }) 157 | } 158 | Type::Array(array) => type_contains_param(&array.elem, param), 159 | Type::Tuple(tuple) => tuple.elems.iter().any(|t| type_contains_param(t, param)), 160 | Type::Paren(paren) => type_contains_param(&paren.elem, param), 161 | Type::BareFn(bare_fn) => { 162 | if bare_fn 163 | .inputs 164 | .iter() 165 | .any(|arg| type_contains_param(&arg.ty, param)) 166 | { 167 | return true; 168 | } 169 | match &bare_fn.output { 170 | ReturnType::Default => false, 171 | ReturnType::Type(_, typ) => type_contains_param(typ, param), 172 | } 173 | } 174 | Type::Reference(reference) => type_contains_param(&reference.elem, param), 175 | Type::Ptr(ptr) => type_contains_param(&ptr.elem, param), 176 | Type::Slice(slice) => type_contains_param(&slice.elem, param), 177 | Type::Never(_) => false, 178 | // Approximation, we'd rather generate wrong code than crash when not needed 179 | Type::Macro(_) | Type::Infer(_) => false, 180 | Type::ImplTrait(_) => unreachable!(), 181 | Type::TraitObject(obj) => obj.bounds.iter().any(|bound| match bound { 182 | TypeParamBound::Trait(t) => type_contains_param( 183 | &Type::Path(TypePath { 184 | qself: None, 185 | path: t.path.clone(), 186 | }), 187 | param, 188 | ), 189 | _ => false, 190 | }), 191 | Type::Verbatim(_) => false, 192 | Type::Group(g) => type_contains_param(&g.elem, param), 193 | _ => abort_call_site!("Found unknown type."), 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /functor_derive_lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("../README.md")] 2 | 3 | use crate::generate_fmap_body::generate_fmap_body; 4 | use crate::map::{map_path, map_where}; 5 | use crate::parse_attribute::parse_attribute; 6 | use proc_macro2::{Ident, TokenStream}; 7 | use proc_macro_error::proc_macro_error; 8 | use quote::{format_ident, quote}; 9 | use syn::token::Colon; 10 | use syn::{ 11 | parse_macro_input, Data, DeriveInput, Expr, ExprPath, GenericArgument, GenericParam, Path, 12 | PathSegment, PredicateType, TraitBound, TraitBoundModifier, Type, TypeParamBound, TypePath, 13 | WhereClause, WherePredicate, 14 | }; 15 | 16 | mod generate_fmap_body; 17 | mod generate_map; 18 | mod map; 19 | mod parse_attribute; 20 | 21 | #[proc_macro_derive(Functor, attributes(functor))] 22 | #[proc_macro_error] 23 | pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 24 | let input = parse_macro_input!(input as DeriveInput); 25 | 26 | // Name of the Struct or Enum we are implementing the `Functor` trait for. 27 | let def_name = input.ident.clone(); 28 | 29 | // Get the attributes for this invocation. If no attributes are given, the first generic is used as default. 30 | let attribute = parse_attribute(&input); 31 | 32 | // Get the generic parameters leaving only the bounds and attributes. 33 | let source_params = input 34 | .generics 35 | .params 36 | .iter() 37 | .map(|param| match param { 38 | GenericParam::Type(param) => { 39 | let mut param = param.clone(); 40 | param.eq_token = None; 41 | param.default = None; 42 | GenericParam::Type(param) 43 | } 44 | GenericParam::Const(param) => { 45 | let mut param = param.clone(); 46 | param.eq_token = None; 47 | param.default = None; 48 | GenericParam::Const(param) 49 | } 50 | param => param.clone(), 51 | }) 52 | .collect::>(); 53 | 54 | // Maps the generic parameters to generic arguments for the source. 55 | let source_args = source_params 56 | .iter() 57 | .map(|param| match param { 58 | GenericParam::Lifetime(l) => GenericArgument::Lifetime(l.lifetime.clone()), 59 | GenericParam::Type(t) => GenericArgument::Type(Type::Path(TypePath { 60 | qself: None, 61 | path: Path::from(PathSegment::from(t.ident.clone())), 62 | })), 63 | GenericParam::Const(c) => GenericArgument::Const(Expr::Path(ExprPath { 64 | attrs: vec![], 65 | qself: None, 66 | path: Path::from(PathSegment::from(c.ident.clone())), 67 | })), 68 | }) 69 | .collect::>(); 70 | 71 | // Lints 72 | let lints = quote! { 73 | #[allow(absolute_paths_not_starting_with_crate)] 74 | #[allow(bare_trait_objects)] 75 | #[allow(deprecated)] 76 | #[allow(drop_bounds)] 77 | #[allow(dyn_drop)] 78 | #[allow(non_camel_case_types)] 79 | #[allow(trivial_bounds)] 80 | #[allow(unused_qualifications)] 81 | #[allow(clippy::allow)] 82 | #[automatically_derived] 83 | }; 84 | 85 | let mut tokens = TokenStream::new(); 86 | 87 | // Include default Functor implementation. 88 | if let Some(default) = attribute.default { 89 | tokens.extend(generate_default_impl( 90 | &default, 91 | &def_name, 92 | &source_params, 93 | &source_args, 94 | &input.generics.where_clause, 95 | &lints, 96 | )); 97 | } 98 | 99 | // Include all named implementations. 100 | for (param, name) in attribute.name_map { 101 | tokens.extend(generate_named_impl( 102 | ¶m, 103 | &name, 104 | &def_name, 105 | &source_params, 106 | &source_args, 107 | &input.generics.where_clause, 108 | &lints, 109 | )); 110 | } 111 | 112 | // Include internal implementations. 113 | tokens.extend(generate_refs_impl( 114 | &input.data, 115 | &def_name, 116 | &source_params, 117 | &source_args, 118 | &input.generics.where_clause, 119 | &lints, 120 | )); 121 | 122 | tokens.into() 123 | } 124 | 125 | fn find_index(source_params: &[GenericParam], ident: &Ident) -> usize { 126 | for (total, param) in source_params.iter().enumerate() { 127 | match param { 128 | GenericParam::Type(t) if &t.ident == ident => return total, 129 | _ => {} 130 | } 131 | } 132 | unreachable!() 133 | } 134 | 135 | fn generate_refs_impl( 136 | data: &Data, 137 | def_name: &Ident, 138 | source_params: &Vec, 139 | source_args: &Vec, 140 | where_clause: &Option, 141 | lints: &TokenStream, 142 | ) -> TokenStream { 143 | let mut tokens = TokenStream::new(); 144 | for param in source_params { 145 | if let GenericParam::Type(t) = param { 146 | let param_ident = t.ident.clone(); 147 | let param_idx = find_index(source_params, &t.ident); 148 | 149 | let functor_trait_ident = format_ident!("Functor{param_idx}"); 150 | let fmap_ident = format_ident!("__fmap_{param_idx}_ref"); 151 | let try_fmap_ident = format_ident!("__try_fmap_{param_idx}_ref"); 152 | 153 | // Generate body of the `fmap` implementation. 154 | let Some(fmap_ref_body) = generate_fmap_body(data, def_name, ¶m_ident, false) 155 | else { 156 | continue; 157 | }; 158 | let Some(try_fmap_ref_body) = generate_fmap_body(data, def_name, ¶m_ident, true) 159 | else { 160 | continue; 161 | }; 162 | 163 | let mut target_args = source_args.clone(); 164 | target_args[param_idx] = GenericArgument::Type(Type::Path(TypePath { 165 | qself: None, 166 | path: Path::from(PathSegment::from(format_ident!("__B"))), 167 | })); 168 | 169 | if let Some(fn_where_clause) = 170 | create_fn_where_clause(where_clause, source_params, ¶m_ident) 171 | { 172 | tokens.extend(quote!( 173 | #lints 174 | impl<#(#source_params),*> #def_name<#(#source_args),*> #where_clause { 175 | pub fn #fmap_ident<__B>(self, __f: &impl Fn(#param_ident) -> __B) -> #def_name<#(#target_args),*> #fn_where_clause { 176 | use ::functor_derive::*; 177 | #fmap_ref_body 178 | } 179 | 180 | pub fn #try_fmap_ident<__B, __E>(self, __f: &impl Fn(#param_ident) -> Result<__B, __E>) -> Result<#def_name<#(#target_args),*>, __E> #fn_where_clause { 181 | use ::functor_derive::*; 182 | Ok(#try_fmap_ref_body) 183 | } 184 | } 185 | )) 186 | } else { 187 | tokens.extend(quote!( 188 | #lints 189 | impl<#(#source_params),*> ::functor_derive::#functor_trait_ident<#param_ident> for #def_name<#(#source_args),*> #where_clause { 190 | type Target<__B> = #def_name<#(#target_args),*>; 191 | 192 | fn #fmap_ident<__B>(self, __f: &impl Fn(#param_ident) -> __B) -> #def_name<#(#target_args),*> { 193 | use ::functor_derive::*; 194 | #fmap_ref_body 195 | } 196 | 197 | fn #try_fmap_ident<__B, __E>(self, __f: &impl Fn(#param_ident) -> Result<__B, __E>) -> Result<#def_name<#(#target_args),*>, __E> { 198 | use ::functor_derive::*; 199 | Ok(#try_fmap_ref_body) 200 | } 201 | } 202 | )) 203 | } 204 | } 205 | } 206 | tokens 207 | } 208 | 209 | fn generate_default_impl( 210 | param: &Ident, 211 | def_name: &Ident, 212 | source_params: &Vec, 213 | source_args: &Vec, 214 | where_clause: &Option, 215 | lints: &TokenStream, 216 | ) -> TokenStream { 217 | let default_idx = find_index(source_params, param); 218 | 219 | // Create generic arguments for the target. We use `__B` for the mapped generic. 220 | let mut target_args = source_args.clone(); 221 | target_args[default_idx] = GenericArgument::Type(Type::Path(TypePath { 222 | qself: None, 223 | path: Path::from(PathSegment::from(format_ident!("__B"))), 224 | })); 225 | 226 | let default_map = format_ident!("__fmap_{default_idx}_ref"); 227 | let default_try_map = format_ident!("__try_fmap_{default_idx}_ref"); 228 | 229 | if let Some(fn_where_clause) = create_fn_where_clause(where_clause, source_params, param) { 230 | quote!( 231 | #lints 232 | impl<#(#source_params),*> #def_name<#(#source_args),*> #where_clause { 233 | pub fn fmap<__B>(self, __f: impl Fn(#param) -> __B) -> #def_name<#(#target_args),*> #fn_where_clause { 234 | use ::functor_derive::*; 235 | self.#default_map(&__f) 236 | } 237 | 238 | pub fn try_fmap<__B, __E>(self, __f: impl Fn(#param) -> Result<__B, __E>) -> Result<#def_name<#(#target_args),*>, __E> #fn_where_clause { 239 | use ::functor_derive::*; 240 | self.#default_try_map(&__f) 241 | } 242 | } 243 | ) 244 | } else { 245 | quote!( 246 | #lints 247 | impl<#(#source_params),*> ::functor_derive::Functor<#param> for #def_name<#(#source_args),*> { 248 | type Target<__B> = #def_name<#(#target_args),*>; 249 | 250 | fn fmap<__B>(self, __f: impl Fn(#param) -> __B) -> #def_name<#(#target_args),*> { 251 | use ::functor_derive::*; 252 | self.#default_map(&__f) 253 | } 254 | 255 | fn try_fmap<__B, __E>(self, __f: impl Fn(#param) -> Result<__B, __E>) -> Result<#def_name<#(#target_args),*>, __E> { 256 | use ::functor_derive::*; 257 | self.#default_try_map(&__f) 258 | } 259 | } 260 | ) 261 | } 262 | } 263 | 264 | fn generate_named_impl( 265 | param: &Ident, 266 | name: &Ident, 267 | def_name: &Ident, 268 | source_params: &Vec, 269 | source_args: &Vec, 270 | where_clause: &Option, 271 | lints: &TokenStream, 272 | ) -> TokenStream { 273 | let default_idx = find_index(source_params, param); 274 | 275 | // Create generic arguments for the target. We use `__B` for the mapped generic. 276 | let mut target_args = source_args.clone(); 277 | target_args[default_idx] = GenericArgument::Type(Type::Path(TypePath { 278 | qself: None, 279 | path: Path::from(PathSegment::from(format_ident!("__B"))), 280 | })); 281 | 282 | let fmap_name = format_ident!("fmap_{name}"); 283 | let try_fmap_name = format_ident!("try_fmap_{name}"); 284 | 285 | let fmap = format_ident!("__fmap_{default_idx}_ref"); 286 | let fmap_try = format_ident!("__try_fmap_{default_idx}_ref"); 287 | 288 | let fn_where_clause = create_fn_where_clause(where_clause, source_params, param); 289 | 290 | quote!( 291 | #lints 292 | impl<#(#source_params),*> #def_name<#(#source_args),*> #where_clause { 293 | pub fn #fmap_name<__B>(self, __f: impl Fn(#param) -> __B) -> #def_name<#(#target_args),*> #fn_where_clause { 294 | use ::functor_derive::*; 295 | self.#fmap(&__f) 296 | } 297 | 298 | pub fn #try_fmap_name<__B, __E>(self, __f: impl Fn(#param) -> Result<__B, __E>) -> Result<#def_name<#(#target_args),*>, __E> #fn_where_clause { 299 | use ::functor_derive::*; 300 | self.#fmap_try(&__f) 301 | } 302 | } 303 | ) 304 | } 305 | 306 | fn create_fn_where_clause( 307 | where_clause: &Option, 308 | source_params: &Vec, 309 | param: &Ident, 310 | ) -> Option { 311 | let mut predicates = where_clause 312 | .iter() 313 | .flat_map(|where_clause| map_where(where_clause, param)) 314 | .flat_map(|where_clause| where_clause.predicates) 315 | .collect::>(); 316 | 317 | for source_param in source_params { 318 | if let GenericParam::Type(typ) = source_param { 319 | if typ.bounds.is_empty() { 320 | continue; 321 | }; 322 | 323 | let bounds = typ 324 | .bounds 325 | .iter() 326 | .cloned() 327 | .flat_map(|bound| { 328 | if let TypeParamBound::Trait(mut trt) = bound { 329 | match trt.modifier { 330 | TraitBoundModifier::Maybe(_) => None, 331 | TraitBoundModifier::None => { 332 | map_path(&mut trt.path, param, &mut false); 333 | Some(TypeParamBound::Trait(trt)) 334 | } 335 | } 336 | } else { 337 | Some(bound) 338 | } 339 | }) 340 | .collect(); 341 | 342 | predicates.push(WherePredicate::Type(PredicateType { 343 | lifetimes: None, 344 | bounded_ty: Type::Path(TypePath { 345 | qself: None, 346 | path: Path { 347 | leading_colon: None, 348 | segments: [PathSegment { 349 | ident: if &typ.ident == param { 350 | format_ident!("__B") 351 | } else { 352 | typ.ident.clone() 353 | }, 354 | arguments: Default::default(), 355 | }] 356 | .into_iter() 357 | .collect(), 358 | }, 359 | }), 360 | colon_token: Colon::default(), 361 | bounds, 362 | })) 363 | } 364 | } 365 | 366 | // Add param: Sized 367 | predicates.push(WherePredicate::Type(PredicateType { 368 | lifetimes: None, 369 | bounded_ty: Type::Path(TypePath { 370 | qself: None, 371 | path: Path { 372 | leading_colon: None, 373 | segments: [PathSegment { 374 | ident: param.clone(), 375 | arguments: Default::default(), 376 | }] 377 | .into_iter() 378 | .collect(), 379 | }, 380 | }), 381 | colon_token: Colon::default(), 382 | bounds: [TypeParamBound::Trait(TraitBound { 383 | paren_token: None, 384 | modifier: TraitBoundModifier::None, 385 | lifetimes: None, 386 | path: Path { 387 | leading_colon: None, 388 | segments: [PathSegment { 389 | ident: format_ident!("Sized"), 390 | arguments: Default::default(), 391 | }] 392 | .into_iter() 393 | .collect(), 394 | }, 395 | })] 396 | .into_iter() 397 | .collect(), 398 | })); 399 | 400 | if predicates.is_empty() { 401 | None 402 | } else { 403 | Some(WhereClause { 404 | where_token: Default::default(), 405 | predicates: predicates.into_iter().collect(), 406 | }) 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /functor_derive_lib/src/map.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Ident; 2 | use quote::format_ident; 3 | use syn::punctuated::Punctuated; 4 | use syn::{ 5 | AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, ReturnType, Token, Type, 6 | TypeParamBound, WhereClause, WherePredicate, 7 | }; 8 | 9 | /// Maps the given parameter `param` in the where clause `where_clause` to `__B`. 10 | pub fn map_where(where_clause: &WhereClause, param: &Ident) -> Option { 11 | let mut clause = where_clause.clone(); 12 | let mut contains_param = false; 13 | 14 | for pred in &mut clause.predicates { 15 | if let WherePredicate::Type(t) = pred { 16 | map_type(&mut t.bounded_ty, param, &mut contains_param); 17 | map_type_param_bounds(&mut t.bounds, param, &mut contains_param); 18 | } 19 | } 20 | 21 | if contains_param { 22 | Some(clause) 23 | } else { 24 | None 25 | } 26 | } 27 | 28 | /// Maps the given parameter `param` in the type paramameter bounds `bounds` to `__B`. 29 | pub fn map_type_param_bounds( 30 | bounds: &mut Punctuated, 31 | param: &Ident, 32 | contains_param: &mut bool, 33 | ) { 34 | for bound in bounds { 35 | if let TypeParamBound::Trait(trt) = bound { 36 | map_path(&mut trt.path, param, contains_param); 37 | } 38 | } 39 | } 40 | 41 | /// Apologies for the long name, we blame the `Syn` crate =). 42 | fn map_angle_bracketed_generic_arguments( 43 | args: &mut AngleBracketedGenericArguments, 44 | param: &Ident, 45 | contains_param: &mut bool, 46 | ) { 47 | for arg in &mut args.args { 48 | match arg { 49 | GenericArgument::Type(t) => map_type(t, param, contains_param), 50 | GenericArgument::AssocType(assoc) => { 51 | map_type(&mut assoc.ty, param, contains_param); 52 | if let Some(generics) = &mut assoc.generics { 53 | map_angle_bracketed_generic_arguments(generics, param, contains_param); 54 | } 55 | } 56 | GenericArgument::Constraint(_) => {} 57 | _ => {} 58 | } 59 | } 60 | } 61 | 62 | /// Maps the given parameter `param` in the path `path` to `__B`. 63 | pub fn map_path(path: &mut Path, param: &Ident, contains_param: &mut bool) { 64 | // Replace top-level ident 65 | if let Some(seg) = path.segments.first_mut() { 66 | if &seg.ident == param { 67 | seg.ident = format_ident!("__B"); 68 | *contains_param = true; 69 | } 70 | } 71 | 72 | for seg in &mut path.segments { 73 | match &mut seg.arguments { 74 | PathArguments::AngleBracketed(args) => { 75 | map_angle_bracketed_generic_arguments(args, param, contains_param); 76 | } 77 | PathArguments::Parenthesized(args) => { 78 | for input in &mut args.inputs { 79 | map_type(input, param, contains_param); 80 | } 81 | 82 | if let ReturnType::Type(_, t) = &mut args.output { 83 | map_type(t, param, contains_param) 84 | } 85 | } 86 | _ => continue, 87 | } 88 | } 89 | } 90 | 91 | /// Maps the given parameter `param` in the type `typ` to `__B`. 92 | fn map_type(typ: &mut Type, param: &Ident, contains_param: &mut bool) { 93 | match typ { 94 | Type::Array(array) => { 95 | map_type(&mut array.elem, param, contains_param); 96 | } 97 | Type::BareFn(fun) => { 98 | for input in &mut fun.inputs { 99 | map_type(&mut input.ty, param, contains_param); 100 | } 101 | 102 | match &mut fun.output { 103 | ReturnType::Default => {} 104 | ReturnType::Type(_, t) => map_type(t, param, contains_param), 105 | } 106 | } 107 | Type::Group(group) => map_type(&mut group.elem, param, contains_param), 108 | Type::ImplTrait(impl_trait) => { 109 | map_type_param_bounds(&mut impl_trait.bounds, param, contains_param); 110 | } 111 | Type::Paren(paren) => map_type(&mut paren.elem, param, contains_param), 112 | Type::Path(path) => map_path(&mut path.path, param, contains_param), 113 | Type::Ptr(ptr) => map_type(&mut ptr.elem, param, contains_param), 114 | Type::Reference(refer) => map_type(&mut refer.elem, param, contains_param), 115 | Type::Slice(slice) => map_type(&mut slice.elem, param, contains_param), 116 | Type::TraitObject(obj) => { 117 | map_type_param_bounds(&mut obj.bounds, param, contains_param); 118 | } 119 | Type::Tuple(tup) => { 120 | for elem in &mut tup.elems { 121 | map_type(elem, param, contains_param); 122 | } 123 | } 124 | _ => {} 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /functor_derive_lib/src/parse_attribute.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span}; 2 | use proc_macro_error::{abort, abort_call_site}; 3 | use quote::format_ident; 4 | use std::collections::HashSet; 5 | use syn::parse::{Parse, ParseStream}; 6 | use syn::punctuated::Punctuated; 7 | use syn::spanned::Spanned; 8 | use syn::{parse, DeriveInput, GenericParam, Meta, Token}; 9 | 10 | pub fn parse_attribute(input: &DeriveInput) -> Attribute { 11 | functor_param_from_attrs(input).unwrap_or_else(|| functor_param_first(input)) 12 | } 13 | 14 | fn functor_param_first(input: &DeriveInput) -> Attribute { 15 | input 16 | .generics 17 | .params 18 | .iter() 19 | .find_map(|param| { 20 | if let GenericParam::Type(typ) = param { 21 | Some(Attribute { 22 | default: Some(typ.ident.clone()), 23 | name_map: vec![], 24 | }) 25 | } else { 26 | None 27 | } 28 | }) 29 | .unwrap_or_else(|| abort_call_site!("Could not find a generic to map!")) 30 | } 31 | 32 | fn functor_param_from_attrs(input: &DeriveInput) -> Option { 33 | let mut functor_attribute = None::<(Attribute, Span)>; 34 | 35 | // Find upto one `functor` attribute. 36 | for attribute in &input.attrs { 37 | if let Meta::List(list) = &attribute.meta { 38 | // If there is more than one segment, it cannot be `functor`. 39 | if list.path.segments.len() > 1 { 40 | continue; 41 | } 42 | let Some(segment) = list.path.segments.first() else { 43 | continue; 44 | }; 45 | if segment.ident != format_ident!("functor") { 46 | continue; 47 | } 48 | // We already found a `functor` attribute! 49 | if let Some((_, span)) = functor_attribute { 50 | abort!(span, "Found two functor attributes",) 51 | } 52 | let span = list.tokens.span(); 53 | let param = parse(list.tokens.clone().into()).unwrap(); 54 | functor_attribute = Some((param, span)); 55 | } 56 | } 57 | 58 | functor_attribute.map(|(param, _)| param) 59 | } 60 | 61 | #[derive(Debug)] 62 | pub struct Attribute { 63 | pub default: Option, 64 | pub name_map: Vec<(Ident, Ident)>, 65 | } 66 | 67 | impl Parse for Attribute { 68 | fn parse(input: ParseStream) -> syn::Result { 69 | let mut default = None; 70 | let mut name_map = Vec::new(); 71 | let mut seen_names = HashSet::new(); 72 | 73 | for sub_attr in Punctuated::::parse_separated_nonempty(input)? { 74 | match sub_attr { 75 | SubAttribute::Default(param) => { 76 | if default.replace(param).is_some() { 77 | abort_call_site!("Two defaults were provided.") 78 | } 79 | } 80 | SubAttribute::NameMap(param, name) => { 81 | if !seen_names.insert(name.to_string()) { 82 | abort_call_site!("Two identical fmap names were provided.") 83 | } 84 | name_map.push((param, name)); 85 | } 86 | } 87 | } 88 | 89 | Ok(Attribute { default, name_map }) 90 | } 91 | } 92 | 93 | enum SubAttribute { 94 | Default(Ident), 95 | NameMap(Ident, Ident), 96 | } 97 | 98 | impl Parse for SubAttribute { 99 | fn parse(input: ParseStream) -> syn::Result { 100 | let param = input.parse::()?; 101 | 102 | let sub_attr = if input.peek(Token![as]) { 103 | input.parse::()?; 104 | let name = input.parse::()?; 105 | SubAttribute::NameMap(param, name) 106 | } else { 107 | SubAttribute::Default(param) 108 | }; 109 | 110 | Ok(sub_attr) 111 | } 112 | } 113 | --------------------------------------------------------------------------------