1#![doc = include_str!("../README.md")]
2
3use leptos_reactive::SignalUpdate;
4use std::{iter, ops};
5
6pub trait Toggle<I> {
8 fn tracked_toggle(&self);
9}
10
11impl<T, I> Toggle<I> for T
12where
13 T: SignalUpdate<I>,
14 I: ops::Not<Output = I> + Clone,
15{
16 #[inline]
17 fn tracked_toggle(&self) {
18 self.update(|v| *v = !v.clone())
19 }
20}
21
22pub trait AddAssign<I, Rhs> {
24 fn tracked_add(&self, rhs: Rhs);
25}
26
27impl<T, I, Rhs> AddAssign<I, Rhs> for T
28where
29 T: SignalUpdate<I>,
30 I: ops::AddAssign<Rhs>,
31{
32 #[inline]
33 fn tracked_add(&self, rhs: Rhs) {
34 self.update(|v| *v += rhs)
35 }
36}
37
38pub trait SubAssign<I, Rhs> {
40 fn tracked_sub(&self, rhs: Rhs);
41}
42
43impl<T, I, Rhs> SubAssign<I, Rhs> for T
44where
45 T: SignalUpdate<I>,
46 I: ops::SubAssign<Rhs>,
47{
48 #[inline]
49 fn tracked_sub(&self, rhs: Rhs) {
50 self.update(|v| *v -= rhs)
51 }
52}
53
54pub trait MulAssign<I, Rhs> {
56 fn tracked_mul(&self, rhs: Rhs);
57}
58
59impl<T, I, Rhs> MulAssign<I, Rhs> for T
60where
61 T: SignalUpdate<I>,
62 I: ops::MulAssign<Rhs>,
63{
64 #[inline]
65 fn tracked_mul(&self, rhs: Rhs) {
66 self.update(|v| *v *= rhs)
67 }
68}
69
70pub trait DivAssign<I, Rhs> {
72 fn tracked_div(&self, rhs: Rhs);
73}
74
75impl<T, I, Rhs> DivAssign<I, Rhs> for T
76where
77 T: SignalUpdate<I>,
78 I: ops::DivAssign<Rhs>,
79{
80 #[inline]
81 fn tracked_div(&self, rhs: Rhs) {
82 self.update(|v| *v /= rhs)
83 }
84}
85
86pub trait Extend<I, A> {
87 fn tracked_extend<C>(&self, iter: C)
88 where
89 C: IntoIterator<Item = A>;
90}
91
92impl<T, I, A> Extend<I, A> for T
93where
94 T: SignalUpdate<I>,
95 I: iter::Extend<A>,
96{
97 fn tracked_extend<C>(&self, iter: C)
98 where
99 C: IntoIterator<Item = A>,
100 {
101 self.update(|v| iter::Extend::extend(v, iter))
102 }
103}
104
105pub trait TrackedVec<V, T> {
106 fn tracked_push(&self, value: T);
107 fn tracked_pop(&self) -> Option<Option<T>>;
108 fn tracked_append(&self, other: &mut Vec<T>);
109 fn tracked_clear(&self);
110 fn tracked_insert(&self, index: usize, element: T);
111 fn tracked_remove(&self, index: usize) -> Option<T>;
112}
113
114impl<V, T> TrackedVec<V, T> for V
115where
116 V: SignalUpdate<Vec<T>>,
117{
118 fn tracked_push(&self, value: T) {
119 self.update(|v| v.push(value))
120 }
121
122 fn tracked_pop(&self) -> Option<Option<T>> {
123 self.try_update(|v| v.pop())
124 }
125
126 fn tracked_append(&self, other: &mut Vec<T>) {
127 self.update(|v| v.append(other))
128 }
129
130 fn tracked_clear(&self) {
131 self.update(|v| v.clear())
132 }
133
134 fn tracked_insert(&self, index: usize, element: T) {
135 self.update(|v| v.insert(index, element))
136 }
137
138 fn tracked_remove(&self, index: usize) -> Option<T> {
139 self.try_update(|v| v.remove(index))
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use leptos_reactive::{create_runtime, create_scope, create_signal, Scope, SignalGet};
146
147 fn with_scope(func: impl Fn(Scope) + 'static) {
148 create_scope(create_runtime(), func).dispose()
149 }
150
151 #[test]
152 fn should_toggle() {
153 with_scope(|cx| {
154 use super::Toggle;
155
156 let (read, write) = create_signal(cx, false);
157
158 write.tracked_toggle();
159
160 assert!(read.get())
161 })
162 }
163
164 #[test]
165 fn should_add() {
166 with_scope(|cx| {
167 use super::AddAssign;
168
169 let (read_i32, write_i32) = create_signal(cx, 57);
170 let (read_str, write_str) = create_signal(cx, String::new());
171
172 write_i32.tracked_add(12);
173 write_str.tracked_add("Hello ");
174 write_str.tracked_add("World!");
175
176 assert_eq!(read_i32.get(), 69);
177 assert_eq!(read_str.get(), "Hello World!");
178 })
179 }
180
181 #[test]
182 fn should_extend() {
183 with_scope(|cx| {
184 use super::Extend;
185
186 let (read_str, write_str) = create_signal(cx, String::new());
187
188 write_str.tracked_extend(vec!["Hello", " World!"]);
189
190 assert_eq!(read_str.get(), "Hello World!");
191 })
192 }
193
194 #[test]
195 fn should_track_vec() {
196 with_scope(|cx| {
197 use super::TrackedVec;
198
199 let (read_vec, write_vec) = create_signal(cx, vec![1, 2, 3]);
200
201 assert_eq!(write_vec.tracked_pop(), Some(Some(3)));
202 assert_eq!(read_vec.get(), vec![1, 2]);
203
204 write_vec.tracked_push(5);
205
206 assert_eq!(read_vec.get(), vec![1, 2, 5]);
207
208 write_vec.tracked_clear();
209
210 assert_eq!(read_vec.get(), Vec::new() as Vec<i32>);
211 })
212 }
213
214}