feather_tui/
callback.rs

1use crate::error::{FtuiResult, FtuiError};
2use std::any::Any;
3
4/// This macro generates a function that take a reference to a `Box<dyn Any>`
5/// as an argument and return nothing. The function body (`$body`) is the code
6/// that will be execute when the callback is trigger.
7///
8/// # Usage
9/// Use for defining functions required to create a `Callback` object,  
10/// 
11/// # Parameters
12/// - `func_name`: An identifier (`ident`) representing the generated function name.
13/// - `arg_name`: An identifier (`ident`) representing the function argument name.
14/// - `body`: A block (`block`) containing the function implementation.
15/// 
16/// # Example
17/// ```rust
18/// // A callback function that accept a u32 an print it out.
19/// cbk_new_callback_func!(print_num, arg, {
20///    println!("{}", cbk::cast_arg::<u32>(arg)?);
21///    Ok(())
22/// });
23/// ```
24#[macro_export]
25macro_rules! cbk_new_callback_func {
26    ($func_name:ident, $arg_name:ident, $body:block) => {
27        fn $func_name(
28            $arg_name: &Option<Box<dyn std::any::Any>>
29        ) -> feather_tui::error::FtuiResult<()> $body
30    };
31}
32
33/// Casts the argument of a callback function to the specified type.
34///
35/// # Parameters
36/// - `arg`: The argument of the callback function.
37///
38/// # Returns
39/// - `Ok(&T)`: The casted argument..
40/// - `Err(FtuiError)`: Returns an error.
41///
42/// # Notes
43/// - This function should only be use in a callback function. 
44///
45/// # Example
46/// ```rust
47/// // A callback function that accept a u32 an print it out.
48/// cbk_new_callback_func!(print_num, arg, {
49///    println!("{}", cbk::cast_arg::<u32>(arg)?);
50///    Ok(())
51/// });
52/// 
53/// Callback::new(print_num, 5u32).call()?; // print 5
54/// Callback::new(print_num, 6u32).call()?; // print 6
55///     
56/// Callback::new(print_num, "String").call()?; // Error (Wrong type)
57/// Callback::no_arg(print_num).call()?;        // Error (No argument)
58/// ```
59pub fn cast_arg<T>(arg: &Option<Box<dyn Any>>) -> FtuiResult<&T> 
60where
61    T: 'static,
62{
63    arg.as_ref()
64        .ok_or(FtuiError::CallbackCastArgNoArgument)?
65        .downcast_ref::<T>()
66        .ok_or(FtuiError::CallbackCastArgWrongType)
67}
68
69/// A generic callback handler for executing functions with stored arguments. 
70/// `Callback` allows you to associate a function with an optional argument and
71/// invoke it later. 
72///
73/// # Usage
74/// `Callback` is use for creating a `Option` component. The callback will be
75/// trigger when the `Option` component is selected.
76pub struct Callback {
77    func: fn(&Option<Box<dyn Any>>) -> FtuiResult<()>,
78    arg: Option<Box<dyn Any>>,
79}
80
81impl Callback {
82    /// Constructs a new `Callback` with an associated argument.
83    ///
84    /// # Parameters
85    /// - `func`: A callback function created using the `cbk_new_callback_func!` macro.  
86    /// - `arg`: The argument value to associate with the `Callback` (`T: 'static`).
87    ///
88    /// # Example
89    /// ```rust
90    /// // Define a callback function using the macro.
91    /// cbk_new_callback_func!(callback_function, arg, {
92    ///     ...
93    /// });
94    ///
95    /// // Create a `Callback` with an associated `u32` value.
96    /// let _ = Callback::new(callback_function, 5u32);
97    /// ```
98    pub fn new<T>(
99        func: fn(&Option<Box<dyn Any>>) -> FtuiResult<()>, arg: T
100    ) -> Self 
101    where
102        T: 'static,
103    {
104        Callback {
105            func,
106            arg: Some(Box::new(arg)),
107        }
108    }
109
110    /// Constructs a new `Callback` without an associated argument.
111    ///
112    /// # Parameters
113    /// - `func`: A callback function created using the `cbk_new_callback_func!` macro.  
114    ///
115    /// # Example
116    /// ```rust
117    /// // Define a callback function using the macro.
118    /// cbk_new_callback_func!(callback_function, arg, {
119    ///     ...
120    /// });
121    ///
122    /// // Create a `Callback` without a associated argument.
123    /// let _ = Callback::no_arg(callback_function);
124    /// ```
125    pub fn no_arg(func: fn(&Option<Box<dyn Any>>) -> FtuiResult<()>) -> Self {
126        Callback {
127            func,
128            arg: None,
129        }
130    }
131
132    /// Invoke the `Callback`. Typically used for testing purposes.
133    ///
134    /// # Returns
135    /// - `Ok(())`: Returns nothing.
136    /// - `Err(FtuiError)`: Returns an error.  
137    ///
138    /// # Example
139    /// ```rust
140    /// // Define a callback function that accepts a `u32` and prints it.
141    /// cbk_new_callback_func!(print_num, arg, {
142    ///     println!("{}", tui::cbk::cast_arg::<u32>(arg)?);
143    ///     Ok(())
144    /// });
145    /// 
146    /// // Create a `Callback` with an argument of 5 and invoke it.
147    /// Callback::new(print_num, 5u32).call()?; // Prints: 5
148    /// ```
149    pub fn call(&self) -> FtuiResult<()> {
150        (self.func)(&self.arg)?;
151        Ok(())
152    }
153
154    /// Updates the argument associated with this `Callback`.
155    ///
156    /// # Parameters
157    /// - `arg`: The new argument value to associate with the `Callback` (`T' static`).
158    ///
159    /// # Example
160    /// ```rust
161    /// // Define a callback function that accepts a `u32` and prints it.
162    /// cbk_new_callback_func!(print_num, arg, {
163    ///     println!("{}", tui::cbk::cast_arg::<u32>(arg)?);
164    ///     Ok(())
165    /// });
166    /// 
167    /// // Create a `Callback` with an initial argument.
168    /// let mut callback = Callback::new(print_num, 5u32); 
169    ///
170    /// callback.call()?; // Prints: 5
171    ///
172    /// // Update the argument to a new value.
173    /// callback.update_arg(6u32);
174    ///
175    /// callback.call()?; // Prints: 6
176    /// ```
177    pub fn update_arg<T>(&mut self, arg: T)
178    where
179        T: 'static
180    {
181        self.arg = Some(Box::new(arg));
182    }
183
184    /// Remove the argument associated with the `Callback`.
185    pub fn remove_arg(&mut self) {
186        self.arg = None;
187    }
188}