1pub use rust_key_paths::{
8 enum_err, enum_ok, enum_some, enum_variant, variant_of, EnumKp, EnumKpType, EnumValueKpType,
9 Kp,
10};
11
12pub type CasePath<'a, Parent, Child> = EnumKpType<'a, Parent, Child>;
14
15pub trait Casepath<Parent, Child> {
17 fn extract(&self, parent: &Parent) -> Option<Child>;
18 fn wrap(&self, child: Child) -> Parent;
19}
20
21impl<Parent, Child, G, S, E> Casepath<Parent, Child>
22 for EnumKp<
23 Parent,
24 Child,
25 &'static Parent,
26 &'static Child,
27 &'static mut Parent,
28 &'static mut Child,
29 G,
30 S,
31 E,
32 >
33where
34 Parent: 'static,
35 Child: Clone + 'static,
36 G: for<'b> Fn(&'b Parent) -> Option<&'b Child>,
37 S: for<'b> Fn(&'b mut Parent) -> Option<&'b mut Child>,
38 E: Fn(Child) -> Parent + Clone,
39{
40 fn extract(&self, parent: &Parent) -> Option<Child> {
41 self.get_ref(parent).cloned()
42 }
43
44 fn wrap(&self, child: Child) -> Parent {
45 self.embed(child)
46 }
47}
48
49#[cfg(test)]
50#[allow(dead_code, clippy::bool_assert_comparison)]
51mod tests {
52 use super::*;
53 use key_paths_core::RefKpTrait;
54 use key_paths_derive::{Cp, Kp};
55
56 #[derive(Debug, Kp, Clone, PartialEq)]
57 struct AppState {
58 counter: Option<CounterState>,
59 label: String,
60 }
61
62 #[derive(Debug, Kp, Clone, PartialEq)]
63 struct CounterState {
64 value: i32,
65 }
66
67 #[derive(Debug, Kp, Clone, PartialEq, Eq, Cp)]
68 enum AppAction {
69 Counter(CounterAction),
70 Reset,
71 }
72
73 #[derive(Debug, Kp, Clone, Copy, PartialEq, Eq, Cp)]
74 enum CounterAction {
75 Increment,
76 Decrement,
77 }
78
79 #[derive(Clone, Debug, PartialEq, Eq, Kp, Cp)]
80 enum RootAction {
81 App(AppAction),
82 }
83
84 #[test]
85 fn ref_kp_trait_focus_round_trip() {
86 let mut state = AppState {
87 counter: Some(CounterState { value: 0 }),
88 label: "app".into(),
89 };
90 let kp = AppState::counter();
91 assert_eq!(kp.focus(&state).map(|c| c.value), Some(0));
92 if let Some(counter) = kp.focus_mut(&mut state) {
93 counter.value = 5;
94 }
95 assert_eq!(state.counter.unwrap().value, 5);
96 }
97
98 #[test]
99 fn casepath_extract_and_wrap_single_level() {
100 let kp = AppAction::counter_cp();
101 let action = AppAction::Counter(CounterAction::Increment);
102 assert_eq!(kp.extract(&action), Some(CounterAction::Increment));
103 assert_eq!(
104 kp.wrap(CounterAction::Decrement),
105 AppAction::Counter(CounterAction::Decrement)
106 );
107 }
108
109 #[test]
110 fn composed_casepath_chains_three_levels() {
111 let kp = RootAction::app_cp()
112 .then(AppAction::counter_cp());
113
114 let root = RootAction::App(AppAction::Counter(CounterAction::Increment));
115 assert_eq!(kp.extract(&root), Some(CounterAction::Increment));
116
117 let rebuilt = kp.wrap(CounterAction::Decrement);
118 assert_eq!(
119 rebuilt,
120 RootAction::App(AppAction::Counter(CounterAction::Decrement))
121 );
122 }
123
124 #[test]
125 fn then_composition_over_option_and_nested_struct() {
126 let mut state = AppState {
127 counter: Some(CounterState { value: 1 }),
128 label: "x".into(),
129 };
130 let value_kp = AppState::counter().then(CounterState::value());
131 assert_eq!(value_kp.get(&state).copied(), Some(1));
132 if let Some(v) = value_kp.get_mut(&mut state) {
133 *v = 99;
134 }
135 assert_eq!(state.counter.unwrap().value, 99);
136 }
137}