1use std::rc::Rc;
2
3pub 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 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, KeyPaths::FailableReadable(f) => f(root),
72 KeyPaths::FailableWritable(_) => None, KeyPaths::ReadableEnum { extract, .. } => extract(root),
74 KeyPaths::WritableEnum { extract, .. } => extract(root),
75 }
76 }
77
78 pub fn get_mut<'a>(&'a self, root: &'a mut Root) -> Option<&'a mut Value> {
80 match self {
81 KeyPaths::Readable(_) => None, KeyPaths::Writable(f) => Some(f(root)),
83 KeyPaths::FailableReadable(_) => None, 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 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 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 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()), 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 (
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 let intermediate_mid_ref = f_root_mid(r);
285
286 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}