leptos_tracked/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use leptos_reactive::SignalUpdate;
4use std::{iter, ops};
5
6/// Toggles this signal between two values
7pub 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
22/// Adds a value to this signal.
23pub 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
38/// Subtracts a value from this signal.
39pub 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
54/// Multiplies a value with this signal.
55pub 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
70/// Divides a value with this signal
71pub 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}