rust_overture/
keypaths.rs

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