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 (FailableReadable(f1), ReadableEnum { extract, .. }, ) => {
201 FailableReadable(Rc::new(move |r| f1(r).and_then(|m| extract(m))))
202 }
203 (ReadableEnum { extract, .. }, Readable(f2)) => {
207 FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
208 }
209
210 (ReadableEnum { extract, .. }, FailableReadable(f2)) => {
211 FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
212 }
213
214 (WritableEnum { extract, .. }, Readable(f2)) => {
215 FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
216 }
217
218 (WritableEnum { extract, .. }, FailableReadable(f2)) => {
219 FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
220 }
221
222 (WritableEnum { extract_mut, .. }, Writable(f2)) => {
223 FailableWritable(Rc::new(move |r| extract_mut(r).map(|m| f2(m))))
224 }
225
226 (
298 FailableWritable(f_root_mid),
299 WritableEnum {
300 extract_mut: exm_mid_val,
301 ..
302 },
303 ) => {
304 FailableWritable(Rc::new(move |r: &mut Root| {
305 let intermediate_mid_ref = f_root_mid(r);
308
309 intermediate_mid_ref.and_then(|intermediate_mid| exm_mid_val(intermediate_mid))
312 }))
313 }
314
315 (WritableEnum { extract_mut, .. }, FailableWritable(f2)) => {
316 FailableWritable(Rc::new(move |r| extract_mut(r).and_then(|m| f2(m))))
317 }
318
319 (Writable(f1), WritableEnum { extract_mut, .. }) => {
321 FailableWritable(Rc::new(move |r: &mut Root| {
322 let mid: &mut Mid = f1(r);
323 extract_mut(mid)
324 }))
325 }
326
327 (
328 ReadableEnum {
329 extract: ex1,
330 embed: em1,
331 },
332 ReadableEnum {
333 extract: ex2,
334 embed: em2,
335 },
336 ) => ReadableEnum {
337 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
338 embed: Rc::new(move |v| em1(em2(v))),
339 },
340
341 (
342 WritableEnum {
343 extract: ex1,
344 extract_mut,
345 embed: em1,
346 },
347 ReadableEnum {
348 extract: ex2,
349 embed: em2,
350 },
351 ) => ReadableEnum {
352 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
353 embed: Rc::new(move |v| em1(em2(v))),
354 },
355
356 (
357 WritableEnum {
358 extract: ex1,
359 extract_mut: exm1,
360 embed: em1,
361 },
362 WritableEnum {
363 extract: ex2,
364 extract_mut: exm2,
365 embed: em2,
366 },
367 ) => WritableEnum {
368 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
369 extract_mut: Rc::new(move |r| exm1(r).and_then(|m| exm2(m))),
370 embed: Rc::new(move |v| em1(em2(v))),
371 },
372
373 (a, b) => panic!(
374 "Unsupported composition: {:?} then {:?}",
375 kind_name(&a),
376 kind_name(&b)
377 ),
378 }
379 }
380}
381
382fn kind_name<Root, Value>(k: &KeyPaths<Root, Value>) -> &'static str {
383 use KeyPaths::*;
384 match k {
385 Readable(_) => "Readable",
386 Writable(_) => "Writable",
387 FailableReadable(_) => "FailableReadable",
388 FailableWritable(_) => "FailableWritable",
389 ReadableEnum { .. } => "ReadableEnum",
390 WritableEnum { .. } => "WritableEnum",
391 }
392}
393
394#[macro_export]
397macro_rules! readable_enum_macro {
398 ($enum:path, $variant:ident) => {{
400 $crate::KeyPaths::readable_enum(
401 |_| <$enum>::$variant,
402 |e: &$enum| match e {
403 <$enum>::$variant => Some(&()),
404 _ => None,
405 },
406 )
407 }};
408 ($enum:path, $variant:ident($inner:ty)) => {{
410 $crate::KeyPaths::readable_enum(
411 |v: $inner| <$enum>::$variant(v),
412 |e: &$enum| match e {
413 <$enum>::$variant(v) => Some(v),
414 _ => None,
415 },
416 )
417 }};
418}
419
420#[macro_export]
421macro_rules! writable_enum_macro {
422 ($enum:path, $variant:ident) => {{
424 $crate::KeyPaths::writable_enum(
425 |_| <$enum>::$variant,
426 |e: &$enum| match e {
427 <$enum>::$variant => Some(&()),
428 _ => None,
429 },
430 |e: &mut $enum| match e {
431 <$enum>::$variant => Some(&mut ()),
432 _ => None,
433 },
434 )
435 }};
436 ($enum:path, $variant:ident($inner:ty)) => {{
438 $crate::KeyPaths::writable_enum(
439 |v: $inner| <$enum>::$variant(v),
440 |e: &$enum| match e {
441 <$enum>::$variant(v) => Some(v),
442 _ => None,
443 },
444 |e: &mut $enum| match e {
445 <$enum>::$variant(v) => Some(v),
446 _ => None,
447 },
448 )
449 }};
450}