Skip to main content

stratum_core/
callback.rs

1use std::fmt;
2use std::sync::Arc;
3
4/// A type-erased, cloneable callback.
5///
6/// Used by components to accept event handler functions from consumers
7/// without coupling to a specific framework's callback type.
8///
9/// Framework adapters convert framework-specific callbacks (e.g., Leptos
10/// `Callback`, Dioxus closures) to and from this type.
11pub struct Callback<T: 'static> {
12    f: Arc<dyn Fn(T) + Send + Sync>,
13}
14
15impl<T: 'static> Callback<T> {
16    /// Create a new callback from a function.
17    pub fn new(f: impl Fn(T) + Send + Sync + 'static) -> Self {
18        Self { f: Arc::new(f) }
19    }
20
21    /// Invoke the callback with the given argument.
22    pub fn call(&self, arg: T) {
23        (self.f)(arg);
24    }
25}
26
27impl<T: 'static> Clone for Callback<T> {
28    fn clone(&self) -> Self {
29        Self {
30            f: Arc::clone(&self.f),
31        }
32    }
33}
34
35impl<T: 'static> fmt::Debug for Callback<T> {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        f.write_str("Callback(..)")
38    }
39}
40
41impl<T: 'static> PartialEq for Callback<T> {
42    fn eq(&self, other: &Self) -> bool {
43        Arc::ptr_eq(&self.f, &other.f)
44    }
45}
46
47/// A callback that takes no arguments.
48pub type ActionCallback = Callback<()>;
49
50/// A callback that receives a boolean value (toggle, checkbox, switch).
51pub type BoolCallback = Callback<bool>;
52
53/// A callback that receives a string value (input change, select).
54pub type StringCallback = Callback<String>;
55
56/// A callback that receives an index (navigation, selection).
57pub type IndexCallback = Callback<usize>;
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use std::sync::atomic::{AtomicBool, Ordering};
63
64    #[test]
65    fn callback_call() {
66        let called = Arc::new(AtomicBool::new(false));
67        let called_clone = Arc::clone(&called);
68
69        let cb = Callback::new(move |_: ()| {
70            called_clone.store(true, Ordering::SeqCst);
71        });
72
73        cb.call(());
74        assert!(called.load(Ordering::SeqCst));
75    }
76
77    #[test]
78    fn callback_clone() {
79        let cb = Callback::new(|x: i32| {
80            let _ = x;
81        });
82        let cb2 = cb.clone();
83        assert_eq!(cb, cb2);
84    }
85
86    #[test]
87    fn callback_debug() {
88        let cb = Callback::new(|_: ()| {});
89        assert_eq!(format!("{:?}", cb), "Callback(..)");
90    }
91
92    #[test]
93    fn callback_with_value() {
94        let received = Arc::new(std::sync::Mutex::new(String::new()));
95        let received_clone = Arc::clone(&received);
96
97        let cb = StringCallback::new(move |val: String| {
98            *received_clone.lock().unwrap() = val;
99        });
100
101        cb.call("hello".to_string());
102        assert_eq!(*received.lock().unwrap(), "hello");
103    }
104}