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 #[inline]
23 pub fn readable(get: impl for<'a> Fn(&'a Root) -> &'a Value + 'static) -> Self {
24 Self::Readable(Rc::new(get))
25 }
26
27 #[inline]
28 pub fn writable(get_mut: impl for<'a> Fn(&'a mut Root) -> &'a mut Value + 'static) -> Self {
29 Self::Writable(Rc::new(get_mut))
30 }
31
32 #[inline]
33 pub fn failable_readable(
34 get: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
35 ) -> Self {
36 Self::FailableReadable(Rc::new(get))
37 }
38
39 #[inline]
40 pub fn failable_writable(
41 get_mut: impl for<'a> Fn(&'a mut Root) -> Option<&'a mut Value> + 'static,
42 ) -> Self {
43 Self::FailableWritable(Rc::new(get_mut))
44 }
45
46 #[inline]
47 pub fn readable_enum(
48 embed: impl Fn(Value) -> Root + 'static,
49 extract: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
50 ) -> Self {
51 Self::ReadableEnum {
52 extract: Rc::new(extract),
53 embed: Rc::new(embed),
54 }
55 }
56
57 #[inline]
58 pub fn writable_enum(
59 embed: impl Fn(Value) -> Root + 'static,
60 extract: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
61 extract_mut: impl for<'a> Fn(&'a mut Root) -> Option<&'a mut Value> + 'static,
62 ) -> Self {
63 Self::WritableEnum {
64 extract: Rc::new(extract),
65 embed: Rc::new(embed),
66 extract_mut: Rc::new(extract_mut),
67 }
68 }
69}
70
71impl<Root, Value> KeyPaths<Root, Value> {
72 #[inline]
74 pub fn get<'a>(&'a self, root: &'a Root) -> Option<&'a Value> {
75 match self {
76 KeyPaths::Readable(f) => Some(f(root)),
77 KeyPaths::Writable(_) => None, KeyPaths::FailableReadable(f) => f(root),
79 KeyPaths::FailableWritable(_) => None, KeyPaths::ReadableEnum { extract, .. } => extract(root),
81 KeyPaths::WritableEnum { extract, .. } => extract(root),
82 }
83 }
84
85 #[inline]
87 pub fn get_mut<'a>(&'a self, root: &'a mut Root) -> Option<&'a mut Value> {
88 match self {
89 KeyPaths::Readable(_) => None, KeyPaths::Writable(f) => Some(f(root)),
91 KeyPaths::FailableReadable(_) => None, KeyPaths::FailableWritable(f) => f(root),
93 KeyPaths::WritableEnum { extract_mut, .. } => extract_mut(root),
94 _ => None,
95 }
96 }
97
98 pub fn embed(&self, value: Value) -> Option<Root>
99 where
100 Value: Clone,
101 {
102 match self {
103 KeyPaths::ReadableEnum { embed, .. } => Some(embed(value)),
104 _ => None,
105 }
106 }
107
108 pub fn embed_mut(&self, value: Value) -> Option<Root>
109 where
110 Value: Clone,
111 {
112 match self {
113 KeyPaths::WritableEnum { embed, .. } => Some(embed(value)),
114 _ => None,
115 }
116 }
117
118 pub fn iter<'a, T>(&'a self, root: &'a Root) -> Option<<&'a Value as IntoIterator>::IntoIter>
120 where
121 &'a Value: IntoIterator<Item = &'a T>,
122 T: 'a,
123 {
124 self.get(root).map(|v| v.into_iter())
125 }
126
127 pub fn iter_mut<'a, T>(
129 &'a self,
130 root: &'a mut Root,
131 ) -> Option<<&'a mut Value as IntoIterator>::IntoIter>
132 where
133 &'a mut Value: IntoIterator<Item = &'a mut T>,
134 T: 'a,
135 {
136 self.get_mut(root).map(|v| v.into_iter())
137 }
138
139 #[inline]
141 pub fn into_iter<T>(self, root: Root) -> Option<<Value as IntoIterator>::IntoIter>
142 where
143 Value: IntoIterator<Item = T> + Clone,
144 {
145 match self {
146 KeyPaths::Readable(f) => Some(f(&root).clone().into_iter()), KeyPaths::Writable(_) => None,
148 KeyPaths::FailableReadable(f) => f(&root).map(|v| v.clone().into_iter()),
149 KeyPaths::FailableWritable(_) => None,
150 KeyPaths::ReadableEnum { extract, .. } => extract(&root).map(|v| v.clone().into_iter()),
151 KeyPaths::WritableEnum { extract, .. } => extract(&root).map(|v| v.clone().into_iter()),
152 }
153 }
154}
155
156impl<Root, Mid> KeyPaths<Root, Mid>
157where
158 Root: 'static,
159 Mid: 'static,
160{
161 #[inline]
163 pub fn then<Value>(self, mid: KeyPaths<Mid, Value>) -> KeyPaths<Root, Value>
164 where
165 Value: 'static,
166 {
167 self.compose(mid)
168 }
169
170 pub fn compose<Value>(self, mid: KeyPaths<Mid, Value>) -> KeyPaths<Root, Value>
171 where
172 Value: 'static,
173 {
174 use KeyPaths::*;
175
176 match (self, mid) {
177 (Readable(f1), Readable(f2)) => Readable(Rc::new(move |r| f2(f1(r)))),
178
179 (Writable(f1), Writable(f2)) => Writable(Rc::new(move |r| f2(f1(r)))),
180
181 (FailableReadable(f1), Readable(f2)) => {
182 FailableReadable(Rc::new(move |r| f1(r).map(|m| f2(m))))
183 }
184
185 (Readable(f1), FailableReadable(f2)) => FailableReadable(Rc::new(move |r| f2(f1(r)))),
186
187 (FailableReadable(f1), FailableReadable(f2)) => {
188 FailableReadable(Rc::new(move |r| f1(r).and_then(|m| f2(m))))
189 }
190
191 (FailableWritable(f1), Writable(f2)) => {
192 FailableWritable(Rc::new(move |r| f1(r).map(|m| f2(m))))
193 }
194
195 (Writable(f1), FailableWritable(f2)) => FailableWritable(Rc::new(move |r| f2(f1(r)))),
196
197 (FailableWritable(f1), FailableWritable(f2)) => {
198 FailableWritable(Rc::new(move |r| f1(r).and_then(|m| f2(m))))
199 }
200
201 (ReadableEnum { extract, .. }, Readable(f2)) => {
202 FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
203 }
204
205 (ReadableEnum { extract, .. }, FailableReadable(f2)) => {
206 FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
207 }
208
209 (WritableEnum { extract, .. }, Readable(f2)) => {
210 FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
211 }
212
213 (WritableEnum { extract, .. }, FailableReadable(f2)) => {
214 FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
215 }
216
217 (WritableEnum { extract_mut, .. }, Writable(f2)) => {
218 FailableWritable(Rc::new(move |r| extract_mut(r).map(|m| f2(m))))
219 }
220
221 (
293 FailableWritable(f_root_mid),
294 WritableEnum {
295 extract_mut: exm_mid_val,
296 ..
297 },
298 ) => {
299 FailableWritable(Rc::new(move |r: &mut Root| {
300 let intermediate_mid_ref = f_root_mid(r);
303
304 intermediate_mid_ref.and_then(|intermediate_mid| exm_mid_val(intermediate_mid))
307 }))
308 }
309
310 (WritableEnum { extract_mut, .. }, FailableWritable(f2)) => {
311 FailableWritable(Rc::new(move |r| extract_mut(r).and_then(|m| f2(m))))
312 }
313
314 (Writable(f1), WritableEnum { extract_mut, .. }) => {
316 FailableWritable(Rc::new(move |r: &mut Root| {
317 let mid: &mut Mid = f1(r);
318 extract_mut(mid)
319 }))
320 }
321
322 (
323 ReadableEnum {
324 extract: ex1,
325 embed: em1,
326 },
327 ReadableEnum {
328 extract: ex2,
329 embed: em2,
330 },
331 ) => ReadableEnum {
332 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
333 embed: Rc::new(move |v| em1(em2(v))),
334 },
335
336 (
337 WritableEnum {
338 extract: ex1,
339 extract_mut,
340 embed: em1,
341 },
342 ReadableEnum {
343 extract: ex2,
344 embed: em2,
345 },
346 ) => ReadableEnum {
347 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
348 embed: Rc::new(move |v| em1(em2(v))),
349 },
350
351 (
352 WritableEnum {
353 extract: ex1,
354 extract_mut: exm1,
355 embed: em1,
356 },
357 WritableEnum {
358 extract: ex2,
359 extract_mut: exm2,
360 embed: em2,
361 },
362 ) => WritableEnum {
363 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
364 extract_mut: Rc::new(move |r| exm1(r).and_then(|m| exm2(m))),
365 embed: Rc::new(move |v| em1(em2(v))),
366 },
367
368 (a, b) => panic!(
369 "Unsupported composition: {:?} then {:?}",
370 kind_name(&a),
371 kind_name(&b)
372 ),
373 }
374 }
375}
376
377fn kind_name<Root, Value>(k: &KeyPaths<Root, Value>) -> &'static str {
378 use KeyPaths::*;
379 match k {
380 Readable(_) => "Readable",
381 Writable(_) => "Writable",
382 FailableReadable(_) => "FailableReadable",
383 FailableWritable(_) => "FailableWritable",
384 ReadableEnum { .. } => "ReadableEnum",
385 WritableEnum { .. } => "WritableEnum",
386 }
387}
388
389#[macro_export]
392macro_rules! readable_enum_macro {
393 ($enum:path, $variant:ident) => {{
395 $crate::KeyPaths::readable_enum(
396 |_| <$enum>::$variant,
397 |e: &$enum| match e { <$enum>::$variant => Some(&()), _ => None },
398 )
399 }};
400 ($enum:path, $variant:ident($inner:ty)) => {{
402 $crate::KeyPaths::readable_enum(
403 |v: $inner| <$enum>::$variant(v),
404 |e: &$enum| match e { <$enum>::$variant(v) => Some(v), _ => None },
405 )
406 }};
407}
408
409#[macro_export]
410macro_rules! writable_enum_macro {
411 ($enum:path, $variant:ident) => {{
413 $crate::KeyPaths::writable_enum(
414 |_| <$enum>::$variant,
415 |e: &$enum| match e { <$enum>::$variant => Some(&()), _ => None },
416 |e: &mut $enum| match e { <$enum>::$variant => Some(&mut ()), _ => None },
417 )
418 }};
419 ($enum:path, $variant:ident($inner:ty)) => {{
421 $crate::KeyPaths::writable_enum(
422 |v: $inner| <$enum>::$variant(v),
423 |e: &$enum| match e { <$enum>::$variant(v) => Some(v), _ => None },
424 |e: &mut $enum| match e { <$enum>::$variant(v) => Some(v), _ => None },
425 )
426 }};
427}
428