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}