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
23impl<Root, Value> KeyPaths<Root, Value> {
24 #[inline]
25 pub fn readable(get: impl for<'a> Fn(&'a Root) -> &'a Value + 'static) -> Self {
26 Self::Readable(Rc::new(get))
27 }
28
29 #[inline]
30 pub fn writable(get_mut: impl for<'a> Fn(&'a mut Root) -> &'a mut Value + 'static) -> Self {
31 Self::Writable(Rc::new(get_mut))
32 }
33
34 #[inline]
35 pub fn failable_readable(
36 get: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
37 ) -> Self {
38 Self::FailableReadable(Rc::new(get))
39 }
40
41 #[inline]
42 pub fn failable_writable(
43 get_mut: impl for<'a> Fn(&'a mut Root) -> Option<&'a mut Value> + 'static,
44 ) -> Self {
45 Self::FailableWritable(Rc::new(get_mut))
46 }
47
48 #[inline]
49 pub fn readable_enum(
50 embed: impl Fn(Value) -> Root + 'static,
51 extract: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
52 ) -> Self {
53 Self::ReadableEnum {
54 extract: Rc::new(extract),
55 embed: Rc::new(embed),
56 }
57 }
58
59 #[inline]
60 pub fn writable_enum(
61 embed: impl Fn(Value) -> Root + 'static,
62 extract: impl for<'a> Fn(&'a Root) -> Option<&'a Value> + 'static,
63 extract_mut: impl for<'a> Fn(&'a mut Root) -> Option<&'a mut Value> + 'static,
64 ) -> Self {
65 Self::WritableEnum {
66 extract: Rc::new(extract),
67 embed: Rc::new(embed),
68 extract_mut: Rc::new(extract_mut),
69 }
70 }
71}
72
73impl<Root, Value> KeyPaths<Root, Value> {
74 #[inline]
76 pub fn get<'a>(&'a self, root: &'a Root) -> Option<&'a Value> {
77 match self {
78 KeyPaths::Readable(f) => Some(f(root)),
79 KeyPaths::Writable(_) => None, KeyPaths::FailableReadable(f) => f(root),
81 KeyPaths::FailableWritable(_) => None, KeyPaths::ReadableEnum { extract, .. } => extract(root),
83 KeyPaths::WritableEnum { extract, .. } => extract(root),
84 }
85 }
86
87 #[inline]
89 pub fn get_mut<'a>(&'a self, root: &'a mut Root) -> Option<&'a mut Value> {
90 match self {
91 KeyPaths::Readable(_) => None, KeyPaths::Writable(f) => Some(f(root)),
93 KeyPaths::FailableReadable(_) => None, KeyPaths::FailableWritable(f) => f(root),
95 KeyPaths::WritableEnum { extract_mut, .. } => extract_mut(root),
96 _ => None,
97 }
98 }
99
100 pub fn embed(&self, value: Value) -> Option<Root>
101 where
102 Value: Clone,
103 {
104 match self {
105 KeyPaths::ReadableEnum { embed, .. } => Some(embed(value)),
106 _ => None,
107 }
108 }
109
110 pub fn embed_mut(&self, value: Value) -> Option<Root>
111 where
112 Value: Clone,
113 {
114 match self {
115 KeyPaths::WritableEnum { embed, .. } => Some(embed(value)),
116 _ => None,
117 }
118 }
119
120 pub fn iter<'a, T>(&'a self, root: &'a Root) -> Option<<&'a Value as IntoIterator>::IntoIter>
122 where
123 &'a Value: IntoIterator<Item = &'a T>,
124 T: 'a,
125 {
126 self.get(root).map(|v| v.into_iter())
127 }
128
129 pub fn iter_mut<'a, T>(
131 &'a self,
132 root: &'a mut Root,
133 ) -> Option<<&'a mut Value as IntoIterator>::IntoIter>
134 where
135 &'a mut Value: IntoIterator<Item = &'a mut T>,
136 T: 'a,
137 {
138 self.get_mut(root).map(|v| v.into_iter())
139 }
140
141 #[inline]
143 pub fn into_iter<T>(self, root: Root) -> Option<<Value as IntoIterator>::IntoIter>
144 where
145 Value: IntoIterator<Item = T> + Clone,
146 {
147 match self {
148 KeyPaths::Readable(f) => Some(f(&root).clone().into_iter()), KeyPaths::Writable(_) => None,
150 KeyPaths::FailableReadable(f) => f(&root).map(|v| v.clone().into_iter()),
151 KeyPaths::FailableWritable(_) => None,
152 KeyPaths::ReadableEnum { extract, .. } => extract(&root).map(|v| v.clone().into_iter()),
153 KeyPaths::WritableEnum { extract, .. } => extract(&root).map(|v| v.clone().into_iter()),
154 }
155 }
156}
157
158impl<Root, Mid> KeyPaths<Root, Mid>
159where
160 Root: 'static,
161 Mid: 'static,
162{
163 #[inline]
165 pub fn then<Value>(self, mid: KeyPaths<Mid, Value>) -> KeyPaths<Root, Value>
166 where
167 Value: 'static,
168 {
169 self.compose(mid)
170 }
171
172 pub fn compose<Value>(self, mid: KeyPaths<Mid, Value>) -> KeyPaths<Root, Value>
173 where
174 Value: 'static,
175 {
176 use KeyPaths::*;
177
178 match (self, mid) {
179 (Readable(f1), Readable(f2)) => Readable(Rc::new(move |r| f2(f1(r)))),
180
181 (Writable(f1), Writable(f2)) => Writable(Rc::new(move |r| f2(f1(r)))),
182
183 (FailableReadable(f1), Readable(f2)) => {
184 FailableReadable(Rc::new(move |r| f1(r).map(|m| f2(m))))
185 }
186
187 (Readable(f1), FailableReadable(f2)) => FailableReadable(Rc::new(move |r| f2(f1(r)))),
188
189 (FailableReadable(f1), FailableReadable(f2)) => {
190 FailableReadable(Rc::new(move |r| f1(r).and_then(|m| f2(m))))
191 }
192
193 (FailableWritable(f1), Writable(f2)) => {
194 FailableWritable(Rc::new(move |r| f1(r).map(|m| f2(m))))
195 }
196
197 (Writable(f1), FailableWritable(f2)) => FailableWritable(Rc::new(move |r| f2(f1(r)))),
198
199 (FailableWritable(f1), FailableWritable(f2)) => {
200 FailableWritable(Rc::new(move |r| f1(r).and_then(|m| f2(m))))
201 }
202 (FailableReadable(f1), ReadableEnum { extract, .. }) => {
203 FailableReadable(Rc::new(move |r| f1(r).and_then(|m| extract(m))))
204 }
205 (ReadableEnum { extract, .. }, Readable(f2)) => {
209 FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
210 }
211
212 (ReadableEnum { extract, .. }, FailableReadable(f2)) => {
213 FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
214 }
215
216 (WritableEnum { extract, .. }, Readable(f2)) => {
217 FailableReadable(Rc::new(move |r| extract(r).map(|m| f2(m))))
218 }
219
220 (WritableEnum { extract, .. }, FailableReadable(f2)) => {
221 FailableReadable(Rc::new(move |r| extract(r).and_then(|m| f2(m))))
222 }
223
224 (WritableEnum { extract_mut, .. }, Writable(f2)) => {
225 FailableWritable(Rc::new(move |r| extract_mut(r).map(|m| f2(m))))
226 }
227
228 (
229 FailableWritable(f_root_mid),
230 WritableEnum {
231 extract_mut: exm_mid_val,
232 ..
233 },
234 ) => {
235 FailableWritable(Rc::new(move |r: &mut Root| {
236 let intermediate_mid_ref = f_root_mid(r);
239
240 intermediate_mid_ref.and_then(|intermediate_mid| exm_mid_val(intermediate_mid))
243 }))
244 }
245
246 (WritableEnum { extract_mut, .. }, FailableWritable(f2)) => {
247 FailableWritable(Rc::new(move |r| extract_mut(r).and_then(|m| f2(m))))
248 }
249
250 (Writable(f1), WritableEnum { extract_mut, .. }) => {
252 FailableWritable(Rc::new(move |r: &mut Root| {
253 let mid: &mut Mid = f1(r);
254 extract_mut(mid)
255 }))
256 }
257
258 (
259 ReadableEnum {
260 extract: ex1,
261 embed: em1,
262 },
263 ReadableEnum {
264 extract: ex2,
265 embed: em2,
266 },
267 ) => ReadableEnum {
268 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
269 embed: Rc::new(move |v| em1(em2(v))),
270 },
271
272 (
273 WritableEnum {
274 extract: ex1,
275 extract_mut,
276 embed: em1,
277 },
278 ReadableEnum {
279 extract: ex2,
280 embed: em2,
281 },
282 ) => ReadableEnum {
283 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
284 embed: Rc::new(move |v| em1(em2(v))),
285 },
286
287 (
288 WritableEnum {
289 extract: ex1,
290 extract_mut: exm1,
291 embed: em1,
292 },
293 WritableEnum {
294 extract: ex2,
295 extract_mut: exm2,
296 embed: em2,
297 },
298 ) => WritableEnum {
299 extract: Rc::new(move |r| ex1(r).and_then(|m| ex2(m))),
300 extract_mut: Rc::new(move |r| exm1(r).and_then(|m| exm2(m))),
301 embed: Rc::new(move |v| em1(em2(v))),
302 },
303
304 (a, b) => panic!(
305 "Unsupported composition: {:?} then {:?}",
306 kind_name(&a),
307 kind_name(&b)
308 ),
309 }
310 }
311}
312
313fn kind_name<Root, Value>(k: &KeyPaths<Root, Value>) -> &'static str {
314 use KeyPaths::*;
315 match k {
316 Readable(_) => "Readable",
317 Writable(_) => "Writable",
318 FailableReadable(_) => "FailableReadable",
319 FailableWritable(_) => "FailableWritable",
320 ReadableEnum { .. } => "ReadableEnum",
321 WritableEnum { .. } => "WritableEnum",
322 }
323}
324
325#[macro_export]
328macro_rules! readable_enum_macro {
329 ($enum:path, $variant:ident) => {{
331 $crate::KeyPaths::readable_enum(
332 |_| <$enum>::$variant,
333 |e: &$enum| match e {
334 <$enum>::$variant => Some(&()),
335 _ => None,
336 },
337 )
338 }};
339 ($enum:path, $variant:ident($inner:ty)) => {{
341 $crate::KeyPaths::readable_enum(
342 |v: $inner| <$enum>::$variant(v),
343 |e: &$enum| match e {
344 <$enum>::$variant(v) => Some(v),
345 _ => None,
346 },
347 )
348 }};
349}
350
351#[macro_export]
352macro_rules! writable_enum_macro {
353 ($enum:path, $variant:ident) => {{
355 $crate::KeyPaths::writable_enum(
356 |_| <$enum>::$variant,
357 |e: &$enum| match e {
358 <$enum>::$variant => Some(&()),
359 _ => None,
360 },
361 |e: &mut $enum| match e {
362 <$enum>::$variant => Some(&mut ()),
363 _ => None,
364 },
365 )
366 }};
367 ($enum:path, $variant:ident($inner:ty)) => {{
369 $crate::KeyPaths::writable_enum(
370 |v: $inner| <$enum>::$variant(v),
371 |e: &$enum| match e {
372 <$enum>::$variant(v) => Some(v),
373 _ => None,
374 },
375 |e: &mut $enum| match e {
376 <$enum>::$variant(v) => Some(v),
377 _ => None,
378 },
379 )
380 }};
381}