1use std::rc::Rc;
2
3#[derive(Clone)]
4pub enum KeyPaths<Root, Value> {
7 Readable(Rc<dyn for<'a> Fn(&'a Root) -> &'a Value>),
8 ReadableEnum {
9 extract: Rc<dyn for<'a> Fn(&'a Root) -> Option<&'a Value>>,
10 embed: Rc<dyn Fn(Value) -> Root>,
11 },
12 FailableReadable(Rc<dyn for<'a> Fn(&'a Root) -> Option<&'a Value>>),
13
14 Writable(Rc<dyn for<'a> Fn(&'a mut Root) -> &'a mut Value>),
15 FailableWritable(Rc<dyn for<'a> Fn(&'a mut Root) -> Option<&'a mut Value>>),
16 WritableEnum {
17 extract: Rc<dyn for<'a> Fn(&'a Root) -> Option<&'a Value>>,
18 extract_mut: Rc<dyn for<'a> Fn(&'a mut Root) -> Option<&'a mut Value>>,
19 embed: Rc<dyn Fn(Value) -> Root>,
20 },
21
22
23
24 Owned(Rc<dyn Fn(Root) -> Value>),
26 FailableOwned(Rc<dyn Fn(Root) -> Option<Value>>),
27}
28
29impl<Root, Value> KeyPaths<Root, Value> {
30 #[inline]
31 pub fn readable(get: impl for<'a> Fn(&'a Root) -> &'a Value + 'static) -> Self {
32 Self::Readable(Rc::new(get))
33 }
34
35 #[inline]
36 pub fn writable(get_mut: impl for<'a> Fn(&'a mut Root) -> &'a mut Value + 'static) -> Self {
37 Self::Writable(Rc::new(get_mut))
38 }
39
40 #[inline]
41 pub fn failable_readable(
42 get: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
43 ) -> Self {
44 Self::FailableReadable(Rc::new(get))
45 }
46
47 #[inline]
48 pub fn failable_writable(
49 get_mut: impl for<'a> Fn(&'a mut Root) -> Option<&'a mut Value> + 'static,
50 ) -> Self {
51 Self::FailableWritable(Rc::new(get_mut))
52 }
53
54 #[inline]
55 pub fn readable_enum(
56 embed: impl Fn(Value) -> Root + 'static,
57 extract: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
58 ) -> Self {
59 Self::ReadableEnum {
60 extract: Rc::new(extract),
61 embed: Rc::new(embed),
62 }
63 }
64
65 #[inline]
66 pub fn writable_enum(
67 embed: impl Fn(Value) -> Root + 'static,
68 extract: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
69 extract_mut: impl for<'a> Fn(&'a mut Root) -> Option<&'a mut Value> + 'static,
70 ) -> Self {
71 Self::WritableEnum {
72 extract: Rc::new(extract),
73 embed: Rc::new(embed),
74 extract_mut: Rc::new(extract_mut),
75 }
76 }
77
78
79 #[inline]
81 pub fn owned(get: impl Fn(Root) -> Value + 'static) -> Self {
82 Self::Owned(Rc::new(get))
83 }
84
85 #[inline]
86 pub fn failable_owned(get: impl Fn(Root) -> Option<Value> + 'static) -> Self {
87 Self::FailableOwned(Rc::new(get))
88 }
89
90 #[inline]
91 pub fn owned_writable(get: impl Fn(Root) -> Value + 'static) -> Self {
92 Self::Owned(Rc::new(get))
93 }
94
95 #[inline]
96 pub fn failable_owned_writable(get: impl Fn(Root) -> Option<Value> + 'static) -> Self {
97 Self::FailableOwned(Rc::new(get))
98 }
99}
100
101impl<Root, Value> KeyPaths<Root, Value> {
102 #[inline]
104 pub fn get<'a>(&'a self, root: &'a Root) -> Option<&'a Value> {
105 match self {
106 KeyPaths::Readable(f) => Some(f(root)),
107 KeyPaths::Writable(_) => None, KeyPaths::FailableReadable(f) => f(root),
109 KeyPaths::FailableWritable(_) => None, KeyPaths::ReadableEnum { extract, .. } => extract(root),
111 KeyPaths::WritableEnum { extract, .. } => extract(root),
112 KeyPaths::Owned(_) => None, KeyPaths::FailableOwned(_) => None, }
116 }
117
118 #[inline]
120 pub fn get_mut<'a>(&'a self, root: &'a mut Root) -> Option<&'a mut Value> {
121 match self {
122 KeyPaths::Readable(_) => None, KeyPaths::Writable(f) => Some(f(root)),
124 KeyPaths::FailableReadable(_) => None, KeyPaths::FailableWritable(f) => f(root),
126 KeyPaths::ReadableEnum { .. } => None, KeyPaths::WritableEnum { extract_mut, .. } => extract_mut(root),
128 KeyPaths::Owned(_) => None, KeyPaths::FailableOwned(_) => None, }
132 }
133
134 pub fn embed(&self, value: Value) -> Option<Root>
135 where
136 Value: Clone,
137 {
138 match self {
139 KeyPaths::ReadableEnum { embed, .. } => Some(embed(value)),
140 _ => None,
141 }
142 }
143
144 pub fn embed_mut(&self, value: Value) -> Option<Root>
145 where
146 Value: Clone,
147 {
148 match self {
149 KeyPaths::WritableEnum { embed, .. } => Some(embed(value)),
150 _ => None,
151 }
152 }
153
154
155 #[inline]
159 pub fn get_owned(self, root: Root) -> Value {
160 match self {
161 KeyPaths::Owned(f) => f(root),
162 _ => panic!("get_owned only works with owned keypaths"),
163 }
164 }
165
166 #[inline]
168 pub fn get_failable_owned(self, root: Root) -> Option<Value> {
169 match self {
170 KeyPaths::FailableOwned(f) => f(root),
171 _ => panic!("get_failable_owned only works with failable owned keypaths"),
172 }
173 }
174
175 pub fn iter<'a, T>(&'a self, root: &'a Root) -> Option<<&'a Value as IntoIterator>::IntoIter>
177 where
178 &'a Value: IntoIterator<Item = &'a T>,
179 T: 'a,
180 {
181 self.get(root).map(|v| v.into_iter())
182 }
183
184 pub fn iter_mut<'a, T>(
186 &'a self,
187 root: &'a mut Root,
188 ) -> Option<<&'a mut Value as IntoIterator>::IntoIter>
189 where
190 &'a mut Value: IntoIterator<Item = &'a mut T>,
191 T: 'a,
192 {
193 self.get_mut(root).map(|v| v.into_iter())
194 }
195
196 #[inline]
198 pub fn into_iter<T>(self, root: Root) -> Option<<Value as IntoIterator>::IntoIter>
199 where
200 Value: IntoIterator<Item = T> + Clone,
201 {
202 match self {
203 KeyPaths::Readable(f) => Some(f(&root).clone().into_iter()), KeyPaths::Writable(_) => None,
205 KeyPaths::FailableReadable(f) => f(&root).map(|v| v.clone().into_iter()),
206 KeyPaths::FailableWritable(_) => None,
207 KeyPaths::ReadableEnum { extract, .. } => extract(&root).map(|v| v.clone().into_iter()),
208 KeyPaths::WritableEnum { extract, .. } => extract(&root).map(|v| v.clone().into_iter()),
209 KeyPaths::Owned(f) => Some(f(root).into_iter()),
211 KeyPaths::FailableOwned(f) => f(root).map(|v| v.into_iter()),
212 }
213 }
214}
215
216impl<Root, Mid> KeyPaths<Root, Mid>
217where
218 Root: 'static,
219 Mid: 'static,
220{
221 #[inline]
223 pub fn then<Value>(self, mid: KeyPaths<Mid, Value>) -> KeyPaths<Root, Value>
224 where
225 Value: 'static,
226 {
227 self.compose(mid)
228 }
229
230 pub fn compose<Value>(self, mid: KeyPaths<Mid, Value>) -> KeyPaths<Root, Value>
231 where
232 Value: 'static,
233 {
234 use KeyPaths::*;
235
236 match (self, mid) {
237 (Readable(f1), Readable(f2)) => Readable(Rc::new(move |r| f2(f1(r)))),
238
239 (Writable(f1), Writable(f2)) => Writable(Rc::new(move |r| f2(f1(r)))),
240
241 (FailableReadable(f1), Readable(f2)) => {
242 FailableReadable(Rc::new(move |r| f1(r).map(|m| f2(m))))
243 }
244
245 (Readable(f1), FailableReadable(f2)) => FailableReadable(Rc::new(move |r| f2(f1(r)))),
246
247 (FailableReadable(f1), FailableReadable(f2)) => {
248 FailableReadable(Rc::new(move |r| f1(r).and_then(|m| f2(m))))
249 }
250
251 (FailableWritable(f1), Writable(f2)) => {
252 FailableWritable(Rc::new(move |r| f1(r).map(|m| f2(m))))
253 }
254
255 (Writable(f1), FailableWritable(f2)) => FailableWritable(Rc::new(move |r| f2(f1(r)))),
256
257 (FailableWritable(f1), FailableWritable(f2)) => {
258 FailableWritable(Rc::new(move |r| f1(r).and_then(|m| f2(m))))
259 }
260 (FailableReadable(f1), ReadableEnum { extract, .. }) => {
261 FailableReadable(Rc::new(move |r| f1(r).and_then(|m| extract(m))))
262 }
263 (ReadableEnum { extract, .. }, Readable(f2)) => {
267 FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
268 }
269
270 (ReadableEnum { extract, .. }, FailableReadable(f2)) => {
271 FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
272 }
273
274 (WritableEnum { extract, .. }, Readable(f2)) => {
275 FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
276 }
277
278 (WritableEnum { extract, .. }, FailableReadable(f2)) => {
279 FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
280 }
281
282 (WritableEnum { extract_mut, .. }, Writable(f2)) => {
283 FailableWritable(Rc::new(move |r| extract_mut(r).map(|m| f2(m))))
284 }
285
286 (
287 FailableWritable(f_root_mid),
288 WritableEnum {
289 extract_mut: exm_mid_val,
290 ..
291 },
292 ) => {
293 FailableWritable(Rc::new(move |r: &mut Root| {
294 let intermediate_mid_ref = f_root_mid(r);
297
298 intermediate_mid_ref.and_then(|intermediate_mid| exm_mid_val(intermediate_mid))
301 }))
302 }
303
304 (WritableEnum { extract_mut, .. }, FailableWritable(f2)) => {
305 FailableWritable(Rc::new(move |r| extract_mut(r).and_then(|m| f2(m))))
306 }
307
308 (Writable(f1), WritableEnum { extract_mut, .. }) => {
310 FailableWritable(Rc::new(move |r: &mut Root| {
311 let mid: &mut Mid = f1(r);
312 extract_mut(mid)
313 }))
314 }
315
316 (
317 ReadableEnum {
318 extract: ex1,
319 embed: em1,
320 },
321 ReadableEnum {
322 extract: ex2,
323 embed: em2,
324 },
325 ) => ReadableEnum {
326 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
327 embed: Rc::new(move |v| em1(em2(v))),
328 },
329
330 (
331 WritableEnum {
332 extract: ex1,
333 extract_mut: _,
334 embed: em1,
335 },
336 ReadableEnum {
337 extract: ex2,
338 embed: em2,
339 },
340 ) => ReadableEnum {
341 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
342 embed: Rc::new(move |v| em1(em2(v))),
343 },
344
345 (
346 WritableEnum {
347 extract: ex1,
348 extract_mut: exm1,
349 embed: em1,
350 },
351 WritableEnum {
352 extract: ex2,
353 extract_mut: exm2,
354 embed: em2,
355 },
356 ) => WritableEnum {
357 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
358 extract_mut: Rc::new(move |r| exm1(r).and_then(|m| exm2(m))),
359 embed: Rc::new(move |v| em1(em2(v))),
360 },
361
362
363 (Owned(f1), Owned(f2)) => {
365 Owned(Rc::new(move |r| f2(f1(r))))
366 }
367 (FailableOwned(f1), Owned(f2)) => {
368 FailableOwned(Rc::new(move |r| f1(r).map(|m| f2(m))))
369 }
370 (Owned(f1), FailableOwned(f2)) => {
371 FailableOwned(Rc::new(move |r| f2(f1(r))))
372 }
373 (FailableOwned(f1), FailableOwned(f2)) => {
374 FailableOwned(Rc::new(move |r| f1(r).and_then(|m| f2(m))))
375 }
376
377 (a, b) => panic!(
382 "Unsupported composition: {:?} then {:?}",
383 kind_name(&a),
384 kind_name(&b)
385 ),
386 }
387 }
388
389 #[inline]
391 pub fn kind_name(&self) -> &'static str {
392 kind_name(self)
393 }
394}
395
396fn kind_name<Root, Value>(k: &KeyPaths<Root, Value>) -> &'static str {
397 use KeyPaths::*;
398 match k {
399 Readable(_) => "Readable",
400 Writable(_) => "Writable",
401 FailableReadable(_) => "FailableReadable",
402 FailableWritable(_) => "FailableWritable",
403 ReadableEnum { .. } => "ReadableEnum",
404 WritableEnum { .. } => "WritableEnum",
405 Owned(_) => "Owned",
407 FailableOwned(_) => "FailableOwned",
408 }
409}
410
411pub fn compose<Root, Mid, Value>(
420 kp1: KeyPaths<Root, Mid>,
421 kp2: KeyPaths<Mid, Value>,
422) -> KeyPaths<Root, Value>
423where
424 Root: 'static,
425 Mid: 'static,
426 Value: 'static,
427{
428 kp1.compose(kp2)
429}
430
431#[macro_export]
434macro_rules! readable_enum_macro {
435 ($enum:path, $variant:ident) => {{
437 $crate::KeyPaths::readable_enum(
438 |_| <$enum>::$variant,
439 |e: &$enum| match e {
440 <$enum>::$variant => Some(&()),
441 _ => None,
442 },
443 )
444 }};
445 ($enum:path, $variant:ident($inner:ty)) => {{
447 $crate::KeyPaths::readable_enum(
448 |v: $inner| <$enum>::$variant(v),
449 |e: &$enum| match e {
450 <$enum>::$variant(v) => Some(v),
451 _ => None,
452 },
453 )
454 }};
455}
456
457#[macro_export]
458macro_rules! writable_enum_macro {
459 ($enum:path, $variant:ident) => {{
461 $crate::KeyPaths::writable_enum(
462 |_| <$enum>::$variant,
463 |e: &$enum| match e {
464 <$enum>::$variant => Some(&()),
465 _ => None,
466 },
467 |e: &mut $enum| match e {
468 <$enum>::$variant => Some(&mut ()),
469 _ => None,
470 },
471 )
472 }};
473 ($enum:path, $variant:ident($inner:ty)) => {{
475 $crate::KeyPaths::writable_enum(
476 |v: $inner| <$enum>::$variant(v),
477 |e: &$enum| match e {
478 <$enum>::$variant(v) => Some(v),
479 _ => None,
480 },
481 |e: &mut $enum| match e {
482 <$enum>::$variant(v) => Some(v),
483 _ => None,
484 },
485 )
486 }};
487}