Skip to main content

swan_core/value/
func.rs

1use super::*;
2
3
4
5
6// ----------------------------------------------------------------------------
7//  BuiltinFn  (a native Rust function callable from Swan)
8// ----------------------------------------------------------------------------
9
10/// A native Rust function that can be called from Swan code.
11///
12/// # Why a newtype instead of a bare `Rc<dyn Fn>`?
13///
14/// 1. **Debug**: `dyn Fn` is not `Debug`; storing a `name` gives meaningful
15///    output in error messages and test output.
16/// 2. **Metadata**: the `name` can be used when building `Undefined` error
17///    values ("function `math.sin` called on a Bool").
18/// 3. **Ergonomics**: `BuiltinFn::new("name", |arg| ...)` is self-documenting.
19///
20/// # Argument convention
21///
22/// The function receives the evaluated right-hand side as a single `Value`.
23/// Multi-argument calls arrive as `Value::Tuple`.  The function is responsible
24/// for destructuring the tuple when it expects more than one argument.
25///
26/// # Return value
27///
28/// The function must never panic.  Return a [`Value::Undefined`] (for example
29/// with [`Value::undef_unary`]) for any input that is invalid.
30///
31/// # Examples
32///
33/// ```
34/// use swan_core::value::{BuiltinFn, Value};
35///
36/// let negate = BuiltinFn::new("negate", |arg| match arg {
37///     Value::Numb(n) => Value::Numb(-n),
38///     other => Value::undef_unary("negate", other, 0),
39/// });
40/// assert_eq!(negate.call(Value::Numb(2.0)), Value::Numb(-2.0));
41/// ```
42#[derive(Clone)]
43pub struct BuiltinFn {
44    /// Human-readable name, used in Debug output and error messages.
45    pub name: &'static str,
46    func: Rc<dyn Fn(Value) -> Value>,
47}
48
49impl BuiltinFn {
50    /// Create a new built-in function from any `Fn(Value) -> Value`.
51    ///
52    /// Both plain function pointers (`fn(Value) -> Value`) and closures are
53    /// accepted because both coerce to `dyn Fn(Value) -> Value`.
54    pub fn new(name: &'static str, f: impl Fn(Value) -> Value + 'static) -> Self {
55        Self { name, func: Rc::new(f) }
56    }
57
58    /// Invoke the built-in with the given argument.
59    #[inline]
60    pub fn call(&self, arg: Value) -> Value {
61        (self.func)(arg)
62    }
63}
64
65impl std::fmt::Debug for BuiltinFn {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        write!(f, "<builtin {}>", self.name)
68    }
69}
70
71