Skip to main content

bubbles/library/
mod.rs

1//! [`FunctionLibrary`] - built-in functions and host-registration API.
2
3mod i18n;
4mod math;
5#[cfg(feature = "rand")]
6mod rand;
7
8use std::collections::HashMap;
9
10use crate::error::{DialogueError, Result};
11use crate::value::Value;
12
13/// A boxed host function callable from dialogue expressions.
14pub type HostFn = Box<dyn Fn(Vec<Value>) -> Result<Value> + Send + Sync + 'static>;
15
16/// Registry of named functions available to expression evaluation.
17///
18/// Built-in functions (`round`, `dice`, `random_range`, …) are pre-registered.
19/// Hosts can add their own via [`FunctionLibrary::register`].
20pub struct FunctionLibrary {
21    fns: HashMap<String, HostFn>,
22}
23
24impl FunctionLibrary {
25    /// Creates a library with the built-in functions pre-registered.
26    #[must_use]
27    pub fn new() -> Self {
28        let mut lib = Self {
29            fns: HashMap::new(),
30        };
31        lib.register_builtins();
32        lib
33    }
34
35    /// Registers a named function.
36    ///
37    /// Replaces any existing function with the same name.
38    pub fn register<F>(&mut self, name: impl Into<String>, f: F)
39    where
40        F: Fn(Vec<Value>) -> Result<Value> + Send + Sync + 'static,
41    {
42        self.fns.insert(name.into(), Box::new(f));
43    }
44
45    /// Calls the function named `name` with `args`, or returns an error if not found.
46    ///
47    /// # Errors
48    /// Returns [`DialogueError::Function`] if the function is unknown or the call fails.
49    pub fn call(&self, name: &str, args: Vec<Value>) -> Result<Value> {
50        self.fns.get(name).map_or_else(
51            || {
52                Err(DialogueError::Function {
53                    name: name.to_owned(),
54                    message: "unknown function".into(),
55                })
56            },
57            |f| f(args),
58        )
59    }
60
61    fn register_builtins(&mut self) {
62        #[cfg(feature = "rand")]
63        self.register_rand_builtins();
64        self.register_math_builtins();
65        self.register_i18n_builtins();
66    }
67}
68
69impl Default for FunctionLibrary {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl std::fmt::Debug for FunctionLibrary {
76    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77        let names: Vec<&str> = self.fns.keys().map(String::as_str).collect();
78        f.debug_struct("FunctionLibrary")
79            .field("functions", &names)
80            .finish()
81    }
82}
83
84#[cfg(test)]
85#[path = "library_tests/mod.rs"]
86mod tests;