overture_core/keypath.rs
1// Keypath utilities for functional programming
2// Equivalent to Swift's keypath functions for property access and modification
3
4use std::cell::RefCell;
5use std::rc::Rc;
6
7/// A keypath represents a path to a property in a data structure.
8/// This is a functional equivalent to Swift's KeyPath.
9pub struct KeyPath<Root, Value> {
10 getter: Rc<dyn Fn(&Root) -> Value>,
11}
12
13impl<Root, Value> Clone for KeyPath<Root, Value> {
14 fn clone(&self) -> Self {
15 KeyPath {
16 getter: self.getter.clone(),
17 }
18 }
19}
20
21impl<Root, Value> KeyPath<Root, Value> {
22 /// Creates a new keypath from a getter function.
23 pub fn new(getter: impl Fn(&Root) -> Value + 'static) -> Self {
24 KeyPath {
25 getter: Rc::new(getter),
26 }
27 }
28}
29
30/// A writable keypath represents a path to a mutable property in a data structure.
31/// This is a functional equivalent to Swift's WritableKeyPath.
32pub struct WritableKeyPath<Root, Value> {
33 getter: Rc<dyn Fn(&Root) -> Value>,
34 setter: Rc<dyn Fn(&mut Root, Value)>,
35}
36
37impl<Root, Value> Clone for WritableKeyPath<Root, Value> {
38 fn clone(&self) -> Self {
39 WritableKeyPath {
40 getter: self.getter.clone(),
41 setter: self.setter.clone(),
42 }
43 }
44}
45
46impl<Root, Value> WritableKeyPath<Root, Value> {
47 /// Creates a new writable keypath from getter and setter functions.
48 pub fn new(
49 getter: impl Fn(&Root) -> Value + 'static,
50 setter: impl Fn(&mut Root, Value) + 'static,
51 ) -> Self {
52 WritableKeyPath {
53 getter: Rc::new(getter),
54 setter: Rc::new(setter),
55 }
56 }
57}
58
59/// A reference-writable keypath represents a path to a property that can be modified through a reference.
60/// This is a functional equivalent to Swift's ReferenceWritableKeyPath.
61pub struct ReferenceWritableKeyPath<Root, Value> {
62 getter: Rc<dyn Fn(&Root) -> Value>,
63 setter: Rc<dyn Fn(&Root, Value)>,
64}
65
66impl<Root, Value> Clone for ReferenceWritableKeyPath<Root, Value> {
67 fn clone(&self) -> Self {
68 ReferenceWritableKeyPath {
69 getter: self.getter.clone(),
70 setter: self.setter.clone(),
71 }
72 }
73}
74
75impl<Root, Value> ReferenceWritableKeyPath<Root, Value> {
76 /// Creates a new reference-writable keypath from getter and setter functions.
77 pub fn new(
78 getter: impl Fn(&Root) -> Value + 'static,
79 setter: impl Fn(&Root, Value) + 'static,
80 ) -> Self {
81 ReferenceWritableKeyPath {
82 getter: Rc::new(getter),
83 setter: Rc::new(setter),
84 }
85 }
86}
87
88/// Produces a getter function for a given key path. Useful for composing property access with functions.
89/// Equivalent to Swift's get<Root, Value>(_ keyPath: KeyPath<Root, Value>) -> (Root) -> Value
90///
91/// # Examples
92/// ```
93/// use overture_core::keypath::{KeyPath, get};
94///
95/// let name_keypath = KeyPath::new(|person: &Person| person.name.clone());
96/// let get_name = get(name_keypath);
97/// let person = Person { name: "Alice".to_string(), age: 30 };
98/// assert_eq!(get_name(person), "Alice");
99/// ```
100pub fn get<Root, Value>(keypath: KeyPath<Root, Value>) -> impl Fn(Root) -> Value
101where
102 Value: Clone + 'static,
103{
104 move |root: Root| (keypath.getter)(&root)
105}
106
107/// Produces an immutable setter function for a given key path. Useful for composing property changes.
108/// Equivalent to Swift's prop<Root, Value>(_ keyPath: WritableKeyPath<Root, Value>) -> (@escaping (Value) -> Value) -> (Root) -> Root
109///
110/// # Examples
111/// ```
112/// use overture_core::keypath::{WritableKeyPath, prop};
113///
114/// let age_keypath = WritableKeyPath::new(
115/// |person: &Person| person.age,
116/// |person: &mut Person, age| person.age = age
117/// );
118/// let update_age = prop(age_keypath);
119/// let double_age = update_age(Box::new(|age| age * 2));
120/// let person = Person { name: "Alice".to_string(), age: 30 };
121/// let updated = double_age(person);
122/// assert_eq!(updated.age, 60);
123/// ```
124pub fn prop<Root, Value>(
125 keypath: WritableKeyPath<Root, Value>,
126) -> impl Fn(Box<dyn Fn(Value) -> Value>) -> Box<dyn Fn(Root) -> Root>
127where
128 Root: Clone + 'static,
129 Value: Clone + 'static,
130{
131 move |update: Box<dyn Fn(Value) -> Value>| {
132 let keypath = keypath.clone();
133 Box::new(move |root: Root| {
134 let mut copy = root.clone();
135 let value = (keypath.getter)(©);
136 let new_value = update(value);
137 (keypath.setter)(&mut copy, new_value);
138 copy
139 })
140 }
141}
142
143/// Produces an immutable setter function for a given key path and update function.
144/// Equivalent to Swift's over<Root, Value>(_ keyPath: WritableKeyPath<Root, Value>, _ update: @escaping (Value) -> Value) -> (Root) -> Root
145///
146/// # Examples
147/// ```
148/// use overture_core::keypath::{WritableKeyPath, over};
149///
150/// let age_keypath = WritableKeyPath::new(
151/// |person: &Person| person.age,
152/// |person: &mut Person, age| person.age = age
153/// );
154/// let double_age = over(age_keypath, |age| age * 2);
155/// let person = Person { name: "Alice".to_string(), age: 30 };
156/// let updated = double_age(person);
157/// assert_eq!(updated.age, 60);
158/// ```
159pub fn over<Root, Value>(
160 keypath: WritableKeyPath<Root, Value>,
161 update: impl Fn(Value) -> Value + 'static,
162) -> impl Fn(Root) -> Root
163where
164 Root: Clone + 'static,
165 Value: Clone + 'static,
166{
167 let prop_fn = prop(keypath);
168 prop_fn(Box::new(update))
169}
170
171/// Produces an immutable setter function for a given key path and constant value.
172/// Equivalent to Swift's set<Root, Value>(_ keyPath: WritableKeyPath<Root, Value>, _ value: Value) -> (Root) -> Root
173///
174/// # Examples
175/// ```
176/// use overture_core::keypath::{WritableKeyPath, set};
177///
178/// let age_keypath = WritableKeyPath::new(
179/// |person: &Person| person.age,
180/// |person: &mut Person, age| person.age = age
181/// );
182/// let set_age_25 = set(age_keypath, 25);
183/// let person = Person { name: "Alice".to_string(), age: 30 };
184/// let updated = set_age_25(person);
185/// assert_eq!(updated.age, 25);
186/// ```
187pub fn set<Root, Value>(
188 keypath: WritableKeyPath<Root, Value>,
189 value: Value,
190) -> impl Fn(Root) -> Root
191where
192 Root: Clone + 'static,
193 Value: Clone + 'static,
194{
195 over(keypath, move |_| value.clone())
196}
197
198// MARK: - Mutation
199
200/// Produces an in-place setter function for a given key path. Useful for composing value property changes efficiently.
201/// Equivalent to Swift's mprop<Root, Value>(_ keyPath: WritableKeyPath<Root, Value>) -> (@escaping (inout Value) -> Void) -> (inout Root) -> Void
202///
203/// # Examples
204/// ```
205/// use overture_core::keypath::{WritableKeyPath, mprop};
206///
207/// let age_keypath = WritableKeyPath::new(
208/// |person: &Person| person.age,
209/// |person: &mut Person, age| person.age = age
210/// );
211/// let mut_update_age = mprop(age_keypath);
212/// let mut double_age = mut_update_age(Box::new(|age| *age *= 2));
213/// let mut person = Person { name: "Alice".to_string(), age: 30 };
214/// double_age(&mut person);
215/// assert_eq!(person.age, 60);
216/// ```
217pub fn mprop<Root, Value>(
218 keypath: WritableKeyPath<Root, Value>,
219) -> impl Fn(Box<dyn FnMut(&mut Value)>) -> Box<dyn FnMut(&mut Root)>
220where
221 Root: 'static,
222 Value: 'static,
223{
224 move |mut update: Box<dyn FnMut(&mut Value)>| {
225 let keypath = keypath.clone();
226 Box::new(move |root: &mut Root| {
227 let value = (keypath.getter)(root);
228 let mut mutable_value = value;
229 update(&mut mutable_value);
230 (keypath.setter)(root, mutable_value);
231 })
232 }
233}
234
235/// Uncurried `mver`. Takes a key path and update function all at once.
236/// Equivalent to Swift's mver<Root, Value>(_ keyPath: WritableKeyPath<Root, Value>, _ update: @escaping (inout Value) -> Void) -> (inout Root) -> Void
237///
238/// # Examples
239/// ```
240/// use overture_core::keypath::{WritableKeyPath, mver};
241///
242/// let age_keypath = WritableKeyPath::new(
243/// |person: &Person| person.age,
244/// |person: &mut Person, age| person.age = age
245/// );
246/// let mut double_age = mver(age_keypath, |age| *age *= 2);
247/// let mut person = Person { name: "Alice".to_string(), age: 30 };
248/// double_age(&mut person);
249/// assert_eq!(person.age, 60);
250/// ```
251pub fn mver<Root, Value>(
252 keypath: WritableKeyPath<Root, Value>,
253 update: impl FnMut(&mut Value) + 'static,
254) -> impl FnMut(&mut Root)
255where
256 Root: 'static,
257 Value: 'static,
258{
259 let mprop_fn = mprop(keypath);
260 let boxed_update = Box::new(update);
261 let mut setter_fn = mprop_fn(boxed_update);
262 move |root| setter_fn(root)
263}
264
265/// Produces a reference-mutable setter function for a given key path to a reference. Useful for composing reference property changes efficiently.
266/// Equivalent to Swift's mprop<Root, Value>(_ keyPath: ReferenceWritableKeyPath<Root, Value>) -> (@escaping (Value) -> Void) -> (Root) -> Void where Value: AnyObject
267///
268/// # Examples
269/// ```
270/// use overture_core::keypath::{ReferenceWritableKeyPath, mprop_ref};
271///
272/// let name_keypath = ReferenceWritableKeyPath::new(
273/// |person: &Person| person.name.clone(),
274/// |person: &Person, name| { /* would modify in place for reference types */ }
275/// );
276/// let mut_update_name = mprop_ref(name_keypath);
277/// let mut uppercase_name = mut_update_name(Box::new(|name| name.make_ascii_uppercase()));
278/// let person = Person { name: "alice".to_string(), age: 30 };
279/// uppercase_name(person);
280/// ```
281pub fn mprop_ref<Root, Value>(
282 keypath: ReferenceWritableKeyPath<Root, Value>,
283) -> impl Fn(Box<dyn Fn(Value)>) -> Box<dyn Fn(Root)>
284where
285 Root: 'static,
286 Value: 'static,
287{
288 move |update: Box<dyn Fn(Value)>| {
289 let keypath = keypath.clone();
290 Box::new(move |root: Root| {
291 let value = (keypath.getter)(&root);
292 update(value);
293 })
294 }
295}
296
297/// Uncurried `mver`. Takes a key path and update function all at once.
298/// Equivalent to Swift's mverObject<Root, Value>(_ keyPath: ReferenceWritableKeyPath<Root, Value>, _ update: @escaping (Value) -> Void) -> (Root) -> Void where Value: AnyObject
299///
300/// # Examples
301/// ```
302/// use overture_core::keypath::{ReferenceWritableKeyPath, mver_object};
303///
304/// let name_keypath = ReferenceWritableKeyPath::new(
305/// |person: &Person| person.name.clone(),
306/// |person: &Person, name| { /* would modify in place for reference types */ }
307/// );
308/// let uppercase_name = mver_object(name_keypath, |name| name.make_ascii_uppercase());
309/// let person = Person { name: "alice".to_string(), age: 30 };
310/// uppercase_name(person);
311/// ```
312pub fn mver_object<Root, Value>(
313 keypath: ReferenceWritableKeyPath<Root, Value>,
314 update: impl Fn(Value) + 'static,
315) -> impl Fn(Root)
316where
317 Root: 'static,
318 Value: 'static,
319{
320 let mprop_fn = mprop_ref(keypath);
321 let boxed_update = Box::new(update);
322 mprop_fn(boxed_update)
323}
324
325/// Produces a reference-mutable setter function for a given key path to a value. Useful for composing reference property changes efficiently.
326/// Equivalent to Swift's mprop<Root, Value>(_ keyPath: ReferenceWritableKeyPath<Root, Value>) -> (@escaping (inout Value) -> Void) -> (Root) -> Void
327///
328/// # Examples
329/// ```
330/// use overture_core::keypath::{ReferenceWritableKeyPath, mprop_ref_mut};
331///
332/// let name_keypath = ReferenceWritableKeyPath::new(
333/// |person: &Person| person.name.clone(),
334/// |person: &Person, name| { /* would modify in place for reference types */ }
335/// );
336/// let mut_update_name = mprop_ref_mut(name_keypath);
337/// let mut uppercase_name = mut_update_name(Box::new(|name| name.make_ascii_uppercase()));
338/// let person = Person { name: "alice".to_string(), age: 30 };
339/// uppercase_name(person);
340/// ```
341pub fn mprop_ref_mut<Root, Value>(
342 keypath: ReferenceWritableKeyPath<Root, Value>,
343) -> impl FnMut(Box<dyn FnMut(&mut Value)>) -> Box<dyn Fn(Root)>
344where
345 Root: 'static,
346 Value: 'static,
347{
348 let keypath = keypath.clone();
349 move |update: Box<dyn FnMut(&mut Value)>| {
350 let keypath = keypath.clone();
351 let update = RefCell::new(update);
352 Box::new(move |root: Root| {
353 let value = (keypath.getter)(&root);
354 let mut mutable_value = value;
355 update.borrow_mut()(&mut mutable_value);
356 (keypath.setter)(&root, mutable_value);
357 })
358 }
359}
360
361/// Uncurried `mver`. Takes a key path and update function all at once.
362/// Equivalent to Swift's mver<Root, Value>(_ keyPath: ReferenceWritableKeyPath<Root, Value>, _ update: @escaping (inout Value) -> Void) -> (Root) -> Void
363///
364/// # Examples
365/// ```
366/// use overture_core::keypath::{ReferenceWritableKeyPath, mver_ref};
367///
368/// let name_keypath = ReferenceWritableKeyPath::new(
369/// |person: &Person| person.name.clone(),
370/// |person: &Person, name| { /* would modify in place for reference types */ }
371/// );
372/// let mut uppercase_name = mver_ref(name_keypath, |name| name.make_ascii_uppercase());
373/// let person = Person { name: "alice".to_string(), age: 30 };
374/// uppercase_name(person);
375/// ```
376pub fn mver_ref<Root, Value>(
377 keypath: ReferenceWritableKeyPath<Root, Value>,
378 update: impl FnMut(&mut Value) + 'static,
379) -> impl Fn(Root)
380where
381 Root: 'static,
382 Value: 'static,
383{
384 let mut mprop_fn = mprop_ref_mut(keypath);
385 let boxed_update = Box::new(update);
386 mprop_fn(boxed_update)
387}
388
389/// Produces a value-mutable setter function for a given key path and new value.
390/// Equivalent to Swift's mut<Root, Value>(_ keyPath: WritableKeyPath<Root, Value>, _ value: Value) -> (inout Root) -> Void
391///
392/// # Examples
393/// ```
394/// use overture_core::keypath::{WritableKeyPath, mut_set};
395///
396/// let age_keypath = WritableKeyPath::new(
397/// |person: &Person| person.age,
398/// |person: &mut Person, age| person.age = age
399/// );
400/// let mut set_age_25 = mut_set(age_keypath, 25);
401/// let mut person = Person { name: "Alice".to_string(), age: 30 };
402/// set_age_25(&mut person);
403/// assert_eq!(person.age, 25);
404/// ```
405pub fn mut_set<Root, Value>(
406 keypath: WritableKeyPath<Root, Value>,
407 value: Value,
408) -> impl FnMut(&mut Root)
409where
410 Root: 'static,
411 Value: Clone + 'static,
412{
413 mver(keypath, move |v| *v = value.clone())
414}
415
416/// Produces a reference-mutable setter function for a given key path and new value.
417/// Equivalent to Swift's mut<Root, Value>(_ keyPath: ReferenceWritableKeyPath<Root, Value>, _ value: Value) -> (Root) -> Void
418///
419/// # Examples
420/// ```
421/// use overture_core::keypath::{ReferenceWritableKeyPath, mut_set_ref};
422///
423/// let name_keypath = ReferenceWritableKeyPath::new(
424/// |person: &Person| person.name.clone(),
425/// |person: &Person, name| { /* would modify in place for reference types */ }
426/// );
427/// let set_name_bob = mut_set_ref(name_keypath, "Bob".to_string());
428/// let person = Person { name: "Alice".to_string(), age: 30 };
429/// set_name_bob(person);
430/// ```
431pub fn mut_set_ref<Root, Value>(
432 keypath: ReferenceWritableKeyPath<Root, Value>,
433 value: Value,
434) -> impl Fn(Root)
435where
436 Root: 'static,
437 Value: Clone + 'static,
438{
439 mver_ref(keypath, move |v| *v = value.clone())
440}
441
442// Legacy aliases for backward compatibility
443#[doc(hidden)]
444pub fn mset<Root, Value>(
445 keypath: WritableKeyPath<Root, Value>,
446 value: Value,
447) -> impl FnMut(&mut Root)
448where
449 Root: 'static,
450 Value: Clone + 'static,
451{
452 mut_set(keypath, value)
453}
454
455#[doc(hidden)]
456pub fn mset_ref<Root, Value>(
457 keypath: ReferenceWritableKeyPath<Root, Value>,
458 value: Value,
459) -> impl Fn(Root)
460where
461 Root: 'static,
462 Value: Clone + 'static,
463{
464 mut_set_ref(keypath, value)
465}