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}