plux_rs/function/
function.rs

1use std::fmt::{Debug, Display};
2
3use crate::variable::Variable;
4
5use super::Arg;
6
7/// Trait for defining callable functions in the plugin system.
8///
9/// The Function trait represents a callable entity that can be invoked with arguments
10/// and returns a result. Functions can be registered with the loader and called by plugins.
11///
12/// # Type Parameters
13///
14/// * `Output` - The return type of the function (must implement Send + Sync)
15///
16/// # Required Methods
17///
18/// * `name` - Returns the function name as a string
19/// * `inputs` - Returns the list of input arguments
20/// * `output` - Returns the output argument (if any)
21/// * `call` - Executes the function with the given arguments
22///
23/// # Example
24///
25/// ```rust
26/// use plux_rs::function::{Function, Arg, DynamicFunction, FunctionOutput};
27/// use plux_rs::variable::{Variable, VariableType};
28///
29/// // Create a simple add function
30/// let add_func = DynamicFunction::new(
31///     "add",
32///     vec![
33///         Arg::new("a", VariableType::I32),
34///         Arg::new("b", VariableType::I32),
35///     ],
36///     Some(Arg::new("result", VariableType::I32)),
37///     |args| -> FunctionOutput {
38///         let a = args[0].parse_ref::<i32>();
39///         let b = args[1].parse_ref::<i32>();
40///         Ok(Some((a + b).into()))
41///     }
42/// );
43///
44/// // Call the function
45/// let result = add_func.call(&[1.into(), 2.into()]);
46/// assert_eq!(result.unwrap(), Some(3.into()));
47/// ```
48pub trait Function: Send + Sync {
49    /// The output type of the function
50    type Output: Send + Sync;
51
52    /// Returns the name of the function.
53    ///
54    /// # Returns
55    ///
56    /// Returns the function name as a String.
57    fn name(&self) -> String;
58
59    /// Returns the input arguments of the function.
60    ///
61    /// # Returns
62    ///
63    /// Returns a vector of Arg describing the function's input parameters.
64    fn inputs(&self) -> Vec<Arg>;
65
66    /// Returns the output argument of the function.
67    ///
68    /// # Returns
69    ///
70    /// Returns `Some(Arg)` if the function has an output, `None` for void functions.
71    fn output(&self) -> Option<Arg>;
72
73    /// Calls the function with the given arguments.
74    ///
75    /// # Parameters
76    ///
77    /// * `args` - Slice of Variable arguments to pass to the function
78    ///
79    /// # Returns
80    ///
81    /// Returns the function's output of type `Self::Output`.
82    fn call(&self, args: &[Variable]) -> Self::Output;
83}
84
85impl<O: Send + Sync> PartialEq for dyn Function<Output = O> {
86    fn eq(&self, other: &Self) -> bool {
87        self.name() == other.name()
88            && self.inputs() == other.inputs()
89            && self.output() == other.output()
90    }
91}
92
93impl<O: Send + Sync> Eq for dyn Function<Output = O> {}
94
95impl<O: Send + Sync> Display for dyn Function<Output = O> {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        //TODO: Implement function descriptions in August
98        // // Comment as function description
99        // write!(f, "# {}\n", self.description);
100
101        // Function
102        write!(
103            f,
104            "{}({}) -> {}",
105            self.name(),
106            self.inputs()
107                .iter()
108                .map(|x| format!("{x}"))
109                .collect::<Vec<_>>()
110                .join(", "),
111            match self.output() {
112                Some(arg) => format!("{}({})", arg.name, arg.ty),
113                None => "void".to_string(),
114            }
115        )
116    }
117}
118
119impl<O: Send + Sync> Debug for dyn Function<Output = O> {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        Display::fmt(self, f)
122    }
123}
124
125/// Standard output type for dynamic functions.
126///
127/// This type alias represents the result of calling a dynamic function.
128/// It can either succeed with an optional Variable result or fail with an error.
129pub type FunctionOutput = Result<Option<Variable>, Box<dyn std::error::Error + Send + Sync>>;
130
131/// A dynamic function that can be called at runtime.
132///
133/// DynamicFunction provides a concrete implementation of the Function trait that
134/// wraps a closure. This allows for runtime creation of functions with dynamic behavior.
135///
136/// # Fields
137///
138/// * `name` - The function name
139/// * `inputs` - List of input arguments
140/// * `output` - Optional output argument
141/// * `ptr` - The function implementation as a boxed closure
142///
143/// # Example
144///
145/// ```rust
146/// use plux_rs::function::{DynamicFunction, Arg, FunctionOutput};
147/// use plux_rs::variable::VariableType;
148///
149/// let multiply = DynamicFunction::new(
150///     "multiply",
151///     vec![
152///         Arg::new("x", VariableType::F64),
153///         Arg::new("y", VariableType::F64),
154///     ],
155///     Some(Arg::new("result", VariableType::F64)),
156///     |args| -> FunctionOutput {
157///         let x = args[0].parse_ref::<f64>();
158///         let y = args[1].parse_ref::<f64>();
159///         Ok(Some((x * y).into()))
160///     }
161/// );
162/// ```
163pub struct DynamicFunction {
164    name: String,
165    inputs: Vec<Arg>,
166    output: Option<Arg>,
167    ptr: Box<dyn Fn(&[Variable]) -> FunctionOutput + Send + Sync>,
168}
169
170impl DynamicFunction {
171    /// Creates a new dynamic function.
172    ///
173    /// # Parameters
174    ///
175    /// * `name` - The function name (will be converted to String)
176    /// * `inputs` - Vector of input arguments
177    /// * `output` - Optional output argument (None for void functions)
178    /// * `ptr` - The function implementation as a closure
179    ///
180    /// # Returns
181    ///
182    /// Returns a new DynamicFunction instance.
183    ///
184    /// # Type Parameters
185    ///
186    /// * `S` - Type that can be converted into String (for the name)
187    /// * `F` - Function type that takes &[Variable] and returns FunctionOutput
188    ///
189    /// # Example
190    ///
191    /// ```rust
192    /// use plux_rs::function::{DynamicFunction, Arg, FunctionOutput};
193    /// use plux_rs::variable::VariableType;
194    ///
195    /// let greet = DynamicFunction::new(
196    ///     "greet",
197    ///     vec![Arg::new("name", VariableType::String)],
198    ///     Some(Arg::new("message", VariableType::String)),
199    ///     |args| -> FunctionOutput {
200    ///         let name = args[0].parse_ref::<String>();
201    ///         Ok(Some(format!("Hello, {}!", name).into()))
202    ///     }
203    /// );
204    /// ```
205    pub fn new<S, F>(name: S, inputs: Vec<Arg>, output: Option<Arg>, ptr: F) -> Self
206    where
207        S: Into<String>,
208        F: Fn(&[Variable]) -> FunctionOutput + Send + Sync + 'static,
209    {
210        Self {
211            name: name.into(),
212            inputs,
213            output,
214            ptr: Box::new(ptr),
215        }
216    }
217}
218
219impl Function for DynamicFunction {
220    type Output = FunctionOutput;
221
222    fn name(&self) -> String {
223        self.name.clone()
224    }
225
226    fn inputs(&self) -> Vec<Arg> {
227        self.inputs.clone()
228    }
229
230    fn output(&self) -> Option<Arg> {
231        self.output.clone()
232    }
233
234    fn call(&self, args: &[Variable]) -> Self::Output {
235        (self.ptr)(args)
236    }
237}
238
239#[test]
240fn function_call() {
241    use crate::variable::VariableType;
242
243    // Creating a function
244    let func = DynamicFunction::new(
245        "add",
246        vec![
247            Arg::new("a", VariableType::I32),
248            Arg::new("b", VariableType::I32),
249        ],
250        Some(Arg::new("c", VariableType::I32)),
251        |args| -> FunctionOutput {
252            let a = args[0].parse_ref::<i32>();
253            let b = args[1].parse_ref::<i32>();
254
255            let c = a + b;
256
257            println!("{} + {} = {}", a, b, c);
258
259            Ok(Some(c.into()))
260        },
261    );
262
263    // Running the function
264    let c = func.call(&[1.into(), 2.into()]);
265
266    assert!(c.is_ok());
267    assert_eq!(c.unwrap(), Some(3.into()));
268}
269
270#[test]
271fn parallel_call() {
272    use crate::variable::VariableType;
273    use std::{sync::Arc, thread, time::Duration};
274
275    // Creating a function
276    let func = DynamicFunction::new(
277        "log",
278        vec![Arg::new("n", VariableType::I32)],
279        None,
280        |args| -> FunctionOutput {
281            let n = args[0].parse_ref::<i32>();
282
283            println!("Step {n}");
284
285            Ok(None)
286        },
287    );
288
289    // Calling the function in multiple threads
290    let func = Arc::new(func);
291
292    let mut handles = vec![];
293    for i in 0..10 {
294        let func = func.clone();
295        let args: Arc<[Variable]> = Arc::new([i.into()]);
296
297        handles.push(thread::spawn(move || {
298            thread::sleep(Duration::from_secs(1));
299
300            let result = func.call(&args.as_ref());
301
302            assert!(result.is_ok());
303            assert_eq!(result.unwrap(), None);
304        }));
305    }
306
307    for handle in handles {
308        handle.join().unwrap();
309    }
310}