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}