1use key_paths_core::{Readable, Writable};
30
31pub use rust_key_paths::{
32 enum_err, enum_ok, enum_some, enum_variant, variant_of, EnumKp, EnumKpType, EnumValueKpType,
33 Kp, KpType,
34};
35
36pub type StateKp<Whole, Part, G, Set> = Kp<
41 Whole,
42 Part,
43 &'static Whole,
44 &'static Part,
45 &'static mut Whole,
46 &'static mut Part,
47 G,
48 Set,
49>;
50
51pub trait StateLens<Whole, Part> {
53 fn focus<'a>(&self, whole: &'a Whole) -> Option<&'a Part>;
54 fn focus_mut<'a>(&self, whole: &'a mut Whole) -> Option<&'a mut Part>;
55}
56
57impl<'a, R, V, G, Set> StateLens<R, V> for Kp<R, V, &'a R, &'a V, &'a mut R, &'a mut V, G, Set>
58where
59 G: for<'b> Fn(&'b R) -> Option<&'b V>,
60 Set: for<'b> Fn(&'b mut R) -> Option<&'b mut V>,
61{
62 fn focus<'b>(&self, whole: &'b R) -> Option<&'b V> {
63 self.get_ref(whole)
64 }
65
66 fn focus_mut<'b>(&self, whole: &'b mut R) -> Option<&'b mut V> {
67 self.get_mut_ref(whole)
68 }
69}
70
71pub type StateKey<'a, Whole, Part> = KpType<'a, Whole, Part>;
73
74pub type CasePath<'a, Parent, Child> = EnumKpType<'a, Parent, Child>;
76
77pub type ActionCase<'a, Whole, Part> = CasePath<'a, Whole, Part>;
79
80pub type ActionEnum<'a, Whole, Part> = CasePath<'a, Whole, Part>;
82
83pub trait Casepath<Parent, Child> {
85 fn extract(&self, parent: &Parent) -> Option<Child>;
86 fn wrap(&self, child: Child) -> Parent;
87}
88
89impl<Parent, Child, G, S, E> Casepath<Parent, Child>
90 for EnumKp<
91 Parent,
92 Child,
93 &'static Parent,
94 &'static Child,
95 &'static mut Parent,
96 &'static mut Child,
97 G,
98 S,
99 E,
100 >
101where
102 Parent: 'static,
103 Child: Clone + 'static,
104 G: for<'b> Fn(&'b Parent) -> Option<&'b Child>,
105 S: for<'b> Fn(&'b mut Parent) -> Option<&'b mut Child>,
106 E: Fn(Child) -> Parent + Clone,
107{
108 fn extract(&self, parent: &Parent) -> Option<Child> {
109 self.get_ref(parent).cloned()
110 }
111
112 fn wrap(&self, child: Child) -> Parent {
113 self.embed(child)
114 }
115}
116
117pub fn extract<'a, Whole, Part, K>(kp: &K, whole: &'a Whole) -> Option<&'a Part>
119where
120 K: Readable<&'a Whole, &'a Part>,
121{
122 kp.get(whole)
123}
124
125pub fn extract_mut<'a, Whole, Part, K>(kp: &K, whole: &'a mut Whole) -> Option<&'a mut Part>
127where
128 K: Writable<&'a mut Whole, &'a mut Part>,
129{
130 Writable::set(kp, whole)
131}
132
133pub fn extract_action<Parent, Child, CP>(casepath: &CP, action: &Parent) -> Option<Child>
135where
136 CP: Casepath<Parent, Child>,
137{
138 casepath.extract(action)
139}
140
141pub fn wrap_action<Parent, Child, CP>(casepath: &CP, child: Child) -> Parent
143where
144 CP: Casepath<Parent, Child>,
145{
146 casepath.wrap(child)
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152 use key_paths_derive::{Cp, Kp};
153
154 #[derive(Debug, Kp, Clone, PartialEq)]
155 struct AppState {
156 counter: Option<CounterState>,
157 label: String,
158 }
159
160 #[derive(Debug, Kp, Clone, PartialEq)]
161 struct CounterState {
162 value: i32,
163 }
164
165 #[derive(Debug, Kp, Clone, PartialEq, Eq, Cp)]
166 enum AppAction {
167 Counter(CounterAction),
168 Reset,
169 }
170
171 #[derive(Debug, Kp, Clone, Copy, PartialEq, Eq, Cp)]
172 enum CounterAction {
173 Increment,
174 Decrement,
175 }
176
177 #[derive(Clone, Debug, PartialEq, Eq, Kp, Cp)]
178 enum RootAction {
179 App(AppAction),
180 }
181
182 #[test]
183 fn state_key_get_and_get_mut_round_trip() {
184 let mut state = AppState {
185 counter: Some(CounterState { value: 0 }),
186 label: "app".into(),
187 };
188 let kp = AppState::counter();
189 assert_eq!(kp.get(&state).map(|c| c.value), Some(0));
190 if let Some(counter) = kp.get_mut(&mut state) {
191 counter.value = 5;
192 }
193 assert_eq!(state.counter.unwrap().value, 5);
194 }
195
196 #[test]
197 fn casepath_extract_and_wrap_single_level() {
198 let kp = AppAction::counter_cp();
199 let action = AppAction::Counter(CounterAction::Increment);
200 assert_eq!(extract_action(&kp, &action), Some(CounterAction::Increment));
201 assert_eq!(
202 wrap_action(&kp, CounterAction::Decrement),
203 AppAction::Counter(CounterAction::Decrement)
204 );
205 }
206
207 #[test]
208 fn composed_casepath_chains_three_levels() {
209 let kp = RootAction::app_cp()
210 .then(AppAction::counter_cp());
211
212 let root = RootAction::App(AppAction::Counter(CounterAction::Increment));
213 assert_eq!(kp.extract(&root), Some(CounterAction::Increment));
214
215 let rebuilt = kp.wrap(CounterAction::Decrement);
216 assert_eq!(
217 rebuilt,
218 RootAction::App(AppAction::Counter(CounterAction::Decrement))
219 );
220 }
221
222 #[test]
223 fn then_composition_over_option_and_nested_struct() {
224 let mut state = AppState {
225 counter: Some(CounterState { value: 1 }),
226 label: "x".into(),
227 };
228 let value_kp = AppState::counter().then(CounterState::value());
229 assert_eq!(value_kp.get(&state).copied(), Some(1));
230 if let Some(v) = value_kp.get_mut(&mut state) {
231 *v = 99;
232 }
233 assert_eq!(state.counter.unwrap().value, 99);
234 }
235}