feather_tui/
trigger.rs

1use crate::error::{FtuiResult, FtuiError};
2use std::any::Any;
3
4/// This macro generates a function that takes a reference to a `Box<dyn Any>`
5/// as an argument and returns a `bool`. The function body (`$body`) determines
6/// whether the condition is met.
7///
8/// # Usage
9/// Use for defining functions required to create a `Trigger` 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/// ```
18/// // Define a trigger function that print the argument than evaluate whether
19/// // the argument is 5
20/// trg_new_trigger_func!(func_name, arg, {
21///     let number = trg::cast_arg::<u32>(arg)?;
22///     println!("{}", number);
23///     *number == 5
24/// });
25/// ```
26#[macro_export]
27macro_rules! trg_new_trigger_func {
28    ($func_name:ident, $arg_name:ident, $body:block) => {
29        fn $func_name(
30            $arg_name: &Option<Box<dyn std::any::Any>>
31        ) -> feather_tui::error::FtuiResult<bool> $body
32    };
33}
34
35/// Casts the argument of a trigger function to the specified type.
36///
37/// # Parameters
38/// - `arg`: The argument of the trigger function.
39///
40/// # Returns
41/// - `Ok(&T)`: The casted argument..
42/// - `Err(FtuiError)`: Returns an error.
43///
44/// # Notes
45/// - This function should only be use in a trigger function. 
46///
47/// # Example
48/// ```rust
49/// // A trigger function that take in a u32 an evaluate whether it is five.
50/// trg_new_trigger_func!(is_five, arg, {
51///     Ok(*trg::cast_arg::<u32>(arg)? == 5)
52/// });
53///
54/// assert_eq!(Trigger::new(is_five, 5u32).check()?, true); // Evaluate to true
55/// assert_eq!(Trigger::new(is_five, 6u32).check()?, false); // Evaluate to false
56///                                           
57/// Trigger::new(is_five, "String").check()?; // Error (Wrong type) 
58/// Trigger::no_arg(is_five).check()?;        // Error (No argument)
59/// ```
60pub fn cast_arg<T>(arg: &Option<Box<dyn Any>>) -> FtuiResult<&T> 
61where
62    T: 'static,
63{
64    arg.as_ref()
65        .ok_or(FtuiError::TriggerCastArgNoArgument)?
66        .downcast_ref::<T>()
67        .ok_or(FtuiError::TriggerCastArgWrongType)
68}
69
70/// A generic trigger handler for evaluating conditions. `Trigger` allows you
71/// to define a condition as a function, associate it with an optional argument,
72/// and check whether the condition is met.
73///
74/// # Usage
75/// Trigger is use for creating a `Selector` object.
76pub struct Trigger {
77    func: fn(&Option<Box<dyn Any>>) -> FtuiResult<bool>,
78    arg: Option<Box<dyn Any>>,
79}
80
81impl Trigger {
82    /// Constructs a new `Trigger` with an associated argument.
83    ///
84    /// # Parameters
85    /// - `func`: A trigger function created using the `trg_new_trigger_func!` macro.  
86    /// - `arg`: The argument value to associate with the `Trigger` (`T: 'static`).
87    ///
88    /// # Example
89    /// ```rust
90    /// // Define a trigger function using the macro.
91    /// trg_new_trigger_func!(trigger_function, arg, {
92    ///     ...
93    /// });
94    ///
95    /// // Create a `Trigger` with an associated `u32` value.
96    /// let _ = Trigger::new(trigger_function, 5u32);
97    /// ```
98    pub fn new<T>(
99        func: fn(&Option<Box<dyn Any>>) -> FtuiResult<bool>, arg: T
100    ) -> Self
101    where
102        T: 'static,
103    {
104        Trigger {
105            func,
106            arg: Some(Box::new(arg)),
107        }
108    }
109
110    /// Constructs a new `Trigger` without an associated argument.
111    ///
112    /// # Parameters
113    /// - `func`: A trigger function created using the `trg_new_trigger_func!` macro.  
114    ///
115    /// # Example
116    /// ```rust
117    /// // Define a trigger function using the macro.
118    /// trg_new_trigger_func!(trigger_function, arg, {
119    ///     ...
120    /// });
121    ///
122    /// // Create a `Trigger` without an associated argument.
123    /// let _ = Trigger::no_arg(trigger_function);
124    /// ```
125    pub fn no_arg(func: fn(&Option<Box<dyn Any>>) -> FtuiResult<bool>) -> Self {
126        Trigger {
127            func,
128            arg: None,
129        }
130    }
131
132    /// Check whether the `Trigger` evaluate to `true` or `false`. Typically used
133    /// for testing purposes.
134    ///
135    /// # Returns
136    /// - `Ok(bool)`: Whether the trigger condition was met.
137    /// - `Err(FtuiError)`: Returns an error.  
138    ///
139    /// # Example
140    /// ```rust
141    /// // A trigger function that take in a u32 an evaluate whether it is five.
142    /// trg_new_trigger_func!(is_five, arg, {
143    ///     Ok(*trg::cast_arg::<u32>(arg)? == 5)
144    /// });
145    ///
146    /// assert_eq!(Trigger::new(is_five, 5u32).check()?, true);  // Evaluates to true
147    /// assert_eq!(Trigger::new(is_five, 6u32).check()?, false); // Evaluates to false
148    /// ```
149    pub fn check(&self) -> FtuiResult<bool> {
150        (self.func)(&self.arg)
151    }
152
153    /// Updates the argument associated with the `Trigger`.  
154    ///
155    /// # Parameters
156    /// - `arg`: The new argument value to associate with the `Trigger` (`T' static`).
157    ///
158    /// # Example
159    /// ```rust
160    /// // Define a trigger function that checks if the argument is 5.
161    /// trg_new_trigger_func!(is_five, arg, {
162    ///     Ok(*trg::cast_arg::<u32>(arg)? == 5)
163    /// });
164    ///
165    /// // Create a `Trigger` with an initial argument value of 5.
166    /// let mut trigger = Trigger::new(is_five, 5u32);
167    ///
168    /// assert_eq!(trigger.check()?, true); // Evaluates to true.
169    ///
170    /// // Update the argument to a different value.
171    /// trigger.update_arg(6u32);
172    ///
173    /// assert_eq!(trigger.check()?, false); // Now evaluates to false.
174    /// ```
175    pub fn update_arg<T>(&mut self, arg: T) 
176    where
177        T: 'static
178    {
179        self.arg = Some(Box::new(arg));
180    }
181
182    /// Remove the argument associated with the `Trigger`.
183    pub fn remove_arg(&mut self) {
184        self.arg = None;
185    }
186}