key_paths_core/
lib.rs

1use std::rc::Rc;
2
3// #[derive(Clone)]
4pub enum KeyPaths<Root, Value> {
5    Readable(Rc<dyn for<'a> Fn(&'a Root) -> &'a Value>),
6    ReadableEnum {
7        extract: Rc<dyn for<'a> Fn(&'a Root) -> Option<&'a Value>>,
8        embed: Rc<dyn Fn(Value) -> Root>,
9    },
10    FailableReadable(Rc<dyn for<'a> Fn(&'a Root) -> Option<&'a Value>>),
11
12    Writable(Rc<dyn for<'a> Fn(&'a mut Root) -> &'a mut Value>),
13    FailableWritable(Rc<dyn for<'a> Fn(&'a mut Root) -> Option<&'a mut Value>>),
14    WritableEnum {
15        extract: Rc<dyn for<'a> Fn(&'a Root) -> Option<&'a Value>>,
16        extract_mut: Rc<dyn for<'a> Fn(&'a mut Root) -> Option<&'a mut Value>>,
17        embed: Rc<dyn Fn(Value) -> Root>,
18    },
19}
20
21impl<Root, Value> KeyPaths<Root, Value> {
22    pub fn readable(get: impl for<'a> Fn(&'a Root) -> &'a Value + 'static) -> Self {
23        Self::Readable(Rc::new(get))
24    }
25
26    pub fn writable(get_mut: impl for<'a> Fn(&'a mut Root) -> &'a mut Value + 'static) -> Self {
27        Self::Writable(Rc::new(get_mut))
28    }
29
30    pub fn failable_readable(
31        get: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
32    ) -> Self {
33        Self::FailableReadable(Rc::new(get))
34    }
35
36    pub fn failable_writable(
37        get_mut: impl for<'a> Fn(&'a mut Root) -> Option<&'a mut Value> + 'static,
38    ) -> Self {
39        Self::FailableWritable(Rc::new(get_mut))
40    }
41
42    pub fn readable_enum(
43        embed: impl Fn(Value) -> Root + 'static,
44        extract: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
45    ) -> Self {
46        Self::ReadableEnum {
47            extract: Rc::new(extract),
48            embed: Rc::new(embed),
49        }
50    }
51
52    pub fn writable_enum(
53        embed: impl Fn(Value) -> Root + 'static,
54        extract: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
55        extract_mut: impl for<'a> Fn(&'a mut Root) -> Option<&'a mut Value> + 'static,
56    ) -> Self {
57        Self::WritableEnum {
58            extract: Rc::new(extract),
59            embed: Rc::new(embed),
60            extract_mut: Rc::new(extract_mut),
61        }
62    }
63}
64
65impl<Root, Value> KeyPaths<Root, Value> {
66    /// Get an immutable reference if possible
67    pub fn get<'a>(&'a self, root: &'a Root) -> Option<&'a Value> {
68        match self {
69            KeyPaths::Readable(f) => Some(f(root)),
70            KeyPaths::Writable(_) => None, // Writable requires mut
71            KeyPaths::FailableReadable(f) => f(root),
72            KeyPaths::FailableWritable(_) => None, // needs mut
73            KeyPaths::ReadableEnum { extract, .. } => extract(root),
74            KeyPaths::WritableEnum { extract, .. } => extract(root),
75        }
76    }
77
78    /// Get a mutable reference if possible
79    pub fn get_mut<'a>(&'a self, root: &'a mut Root) -> Option<&'a mut Value> {
80        match self {
81            KeyPaths::Readable(_) => None, // immutable only
82            KeyPaths::Writable(f) => Some(f(root)),
83            KeyPaths::FailableReadable(_) => None, // immutable only
84            KeyPaths::FailableWritable(f) => f(root),
85            KeyPaths::WritableEnum { extract_mut, .. } => extract_mut(root),
86            _ => None,
87        }
88    }
89
90    pub fn embed(&self, value: Value) -> Option<Root>
91    where
92        Value: Clone,
93    {
94        match self {
95            KeyPaths::ReadableEnum { embed, .. } => Some(embed(value)),
96            _ => None,
97        }
98    }
99
100    pub fn embed_mut(&self, value: Value) -> Option<Root>
101    where
102        Value: Clone,
103    {
104        match self {
105            KeyPaths::WritableEnum { embed, .. } => Some(embed(value)),
106            _ => None,
107        }
108    }
109
110    /// Iter over immutable references if `Value: IntoIterator`
111    pub fn iter<'a, T>(&'a self, root: &'a Root) -> Option<<&'a Value as IntoIterator>::IntoIter>
112    where
113        &'a Value: IntoIterator<Item = &'a T>,
114        T: 'a,
115    {
116        self.get(root).map(|v| v.into_iter())
117    }
118
119    /// Iter over mutable references if `&mut Value: IntoIterator`
120    pub fn iter_mut<'a, T>(
121        &'a self,
122        root: &'a mut Root,
123    ) -> Option<<&'a mut Value as IntoIterator>::IntoIter>
124    where
125        &'a mut Value: IntoIterator<Item = &'a mut T>,
126        T: 'a,
127    {
128        self.get_mut(root).map(|v| v.into_iter())
129    }
130
131    /// Consume root and iterate if `Value: IntoIterator`
132    pub fn into_iter<T>(self, root: Root) -> Option<<Value as IntoIterator>::IntoIter>
133    where
134        Value: IntoIterator<Item = T> + Clone,
135    {
136        match self {
137            KeyPaths::Readable(f) => Some(f(&root).clone().into_iter()), // requires Clone
138            KeyPaths::Writable(_) => None,
139            KeyPaths::FailableReadable(f) => f(&root).map(|v| v.clone().into_iter()),
140            KeyPaths::FailableWritable(_) => None,
141            KeyPaths::ReadableEnum { extract, .. } => extract(&root).map(|v| v.clone().into_iter()),
142            KeyPaths::WritableEnum { extract, .. } => extract(&root).map(|v| v.clone().into_iter()),
143        }
144    }
145}
146
147impl<Root, Mid> KeyPaths<Root, Mid>
148where
149    Root: 'static,
150    Mid: 'static,
151{
152    pub fn compose<Value>(self, mid: KeyPaths<Mid, Value>) -> KeyPaths<Root, Value>
153    where
154        Value: 'static,
155    {
156        use KeyPaths::*;
157
158        match (self, mid) {
159            (Readable(f1), Readable(f2)) => Readable(Rc::new(move |r| f2(f1(r)))),
160
161            (Writable(f1), Writable(f2)) => Writable(Rc::new(move |r| f2(f1(r)))),
162
163            (FailableReadable(f1), Readable(f2)) => {
164                FailableReadable(Rc::new(move |r| f1(r).map(|m| f2(m))))
165            }
166
167            (Readable(f1), FailableReadable(f2)) => FailableReadable(Rc::new(move |r| f2(f1(r)))),
168
169            (FailableReadable(f1), FailableReadable(f2)) => {
170                FailableReadable(Rc::new(move |r| f1(r).and_then(|m| f2(m))))
171            }
172
173            (FailableWritable(f1), Writable(f2)) => {
174                FailableWritable(Rc::new(move |r| f1(r).map(|m| f2(m))))
175            }
176
177            (Writable(f1), FailableWritable(f2)) => FailableWritable(Rc::new(move |r| f2(f1(r)))),
178
179            (FailableWritable(f1), FailableWritable(f2)) => {
180                FailableWritable(Rc::new(move |r| f1(r).and_then(|m| f2(m))))
181            }
182
183            (ReadableEnum { extract, .. }, Readable(f2)) => {
184                FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
185            }
186
187            (ReadableEnum { extract, .. }, FailableReadable(f2)) => {
188                FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
189            }
190
191            (WritableEnum { extract, .. }, Readable(f2)) => {
192                FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
193            }
194
195            (WritableEnum { extract, .. }, FailableReadable(f2)) => {
196                FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
197            }
198
199            (WritableEnum { extract_mut, .. }, Writable(f2)) => {
200                FailableWritable(Rc::new(move |r| extract_mut(r).map(|m| f2(m))))
201            }
202
203            // (FailableWritable(f2), WritableEnum { extract_mut, .. }) => {
204            //     // FailableWritable(Rc::new(move |r|
205            //     //     {
206            //     //         // let mut x = extract_mut(r);
207            //     //         // x.as_mut().map(|m| f2(m))
208            //     //         // extract_mut(r).map(|m| f2(m))
209            //     //         // extract_mut(r).and_then(|m| f2(m))
210            //     //         // let x = f2(m);
211            //     //         extract_mut(r).and_then(|a| f2(a))
212            //     //
213            //     //     }
214            //     //
215            //     // ))
216            //     // FailableWritable(Rc::new(move |r| extract_mut(r).and_then(|a| f2(a))))
217            //     // FailableWritable(Rc::new(move |r: &mut Root| {
218            //     //     match extract_mut(r) {
219            //     //         Some(mid) => f2(mid), // mid: &mut Mid -> Option<&mut Value>
220            //     //         None => None,
221            //     //     }
222            //     // }) as Rc<dyn for<'a> Fn(&'a mut Root) -> Option<&'a mut Value>>)
223            //
224            //     FailableWritable(Rc::new(move |r: &mut Root| {
225            //         // First extract the intermediate value using extract_mut
226            //         extract_mut(r).and_then(|intermediate| {
227            //             // Now apply f2 to the intermediate value
228            //             // f2 expects &mut Value but intermediate is &mut Value
229            //             f2(intermediate)
230            //         })
231            //     }))
232            // }
233
234            // (WritableEnum { extract_mut, .. }, FailableWritable(f2)) => {
235            //     FailableWritable(Rc::new(move |r: &mut Root| {
236            //         // Extract the intermediate Mid value
237            //         let mid_ref = extract_mut(r)?;
238            //         // Apply the second function to get the final Value
239            //         f2(mid_ref)
240            //     }))
241            // }
242
243            // (FailableWritable(f2), WritableEnum { extract_mut, .. }) => {
244            //     FailableWritable(Rc::new(move |r: &mut Root| {
245            //         // Extract the intermediate Mid value
246            //         let mid_ref = extract_mut(r)?;
247            //         // Apply the second function to get the final Value
248            //         f2(mid_ref)
249            //     }))
250            // }
251
252            /*            (FailableWritable(f2), WritableEnum { extract_mut, .. }) => {
253                            FailableWritable(Rc::new(move |r: &mut Root| {
254                                extract_mut(r).and_then(|intermediate_mid| f2(intermediate_mid))
255                            }))
256                        }
257            */
258            // (FailableWritable(f2), WritableEnum { extract_mut, .. }) => {
259            //     // Here's the fix: f2 must be a function that operates on a Mid and returns a Value
260            //     // It is already of this type since the 'mid' KeyPaths is KeyPaths<Mid, Value>
261            //     FailableWritable(Rc::new(move |r: &mut Root| {
262            //         extract_mut(r).and_then(|intermediate_mid| f2(intermediate_mid))
263            //     }))
264            // }
265
266            // (FailableWritable(f2), WritableEnum { extract_mut, .. }) => {
267            //     FailableWritable(Rc::new(move |r: &mut Root| -> Option<&mut Value> {
268            //         // Extract the intermediate Mid value
269            //         let mid_ref: &mut Mid = extract_mut(r).unwrap();
270            //         // Apply the second function to get the final Value
271            //         f2(mid_ref)
272            //     }))
273            // }
274            (
275                FailableWritable(f_root_mid),
276                WritableEnum {
277                    extract_mut: exm_mid_val,
278                    ..
279                },
280            ) => {
281                FailableWritable(Rc::new(move |r: &mut Root| {
282                    // First, apply the function that operates on Root.
283                    // This will give us `Option<&mut Mid>`.
284                    let intermediate_mid_ref = f_root_mid(r);
285
286                    // Then, apply the function that operates on Mid.
287                    // This will give us `Option<&mut Value>`.
288                    intermediate_mid_ref.and_then(|intermediate_mid| exm_mid_val(intermediate_mid))
289                }))
290            }
291
292            (WritableEnum { extract_mut, .. }, FailableWritable(f2)) => {
293                FailableWritable(Rc::new(move |r| extract_mut(r).and_then(|m| f2(m))))
294            }
295
296            (
297                ReadableEnum {
298                    extract: ex1,
299                    embed: em1,
300                },
301                ReadableEnum {
302                    extract: ex2,
303                    embed: em2,
304                },
305            ) => ReadableEnum {
306                extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
307                embed: Rc::new(move |v| em1(em2(v))),
308            },
309
310            (
311                WritableEnum {
312                    extract: ex1,
313                    extract_mut,
314                    embed: em1,
315                },
316                ReadableEnum {
317                    extract: ex2,
318                    embed: em2,
319                },
320            ) => ReadableEnum {
321                extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
322                embed: Rc::new(move |v| em1(em2(v))),
323            },
324
325            (
326                WritableEnum {
327                    extract: ex1,
328                    extract_mut: exm1,
329                    embed: em1,
330                },
331                WritableEnum {
332                    extract: ex2,
333                    extract_mut: exm2,
334                    embed: em2,
335                },
336            ) => WritableEnum {
337                extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
338                extract_mut: Rc::new(move |r| exm1(r).and_then(|m| exm2(m))),
339                embed: Rc::new(move |v| em1(em2(v))),
340            },
341
342            (a, b) => panic!(
343                "Unsupported composition: {:?} then {:?}",
344                kind_name(&a),
345                kind_name(&b)
346            ),
347        }
348    }
349}
350
351fn kind_name<Root, Value>(k: &KeyPaths<Root, Value>) -> &'static str {
352    use KeyPaths::*;
353    match k {
354        Readable(_) => "Readable",
355        Writable(_) => "Writable",
356        FailableReadable(_) => "FailableReadable",
357        FailableWritable(_) => "FailableWritable",
358        ReadableEnum { .. } => "ReadableEnum",
359        WritableEnum { .. } => "WritableEnum",
360    }
361}