Skip to main content

jetro_core/eval/
methods.rs

1/// v2 custom method registry — similar to v1's `Callable` / `FuncRegistry`.
2///
3/// Users implement [`Method`] (or pass a closure) and register it by name.
4/// Registered methods are checked after all built-ins in `dispatch_method`.
5use std::sync::Arc;
6use indexmap::IndexMap;
7
8use super::value::Val;
9use super::EvalError;
10
11// ── Method trait ──────────────────────────────────────────────────────────────
12
13/// A custom method that can be registered with [`MethodRegistry`].
14///
15/// `recv`  — the value the method was called on
16/// `args`  — positional arguments, already evaluated to `Val`
17pub trait Method: Send + Sync {
18    fn call(&self, recv: Val, args: &[Val]) -> Result<Val, EvalError>;
19}
20
21/// Blanket impl: any `Fn(Val, &[Val]) -> Result<Val, EvalError>` is a `Method`.
22impl<F> Method for F
23where
24    F: Fn(Val, &[Val]) -> Result<Val, EvalError> + Send + Sync,
25{
26    #[inline]
27    fn call(&self, recv: Val, args: &[Val]) -> Result<Val, EvalError> {
28        self(recv, args)
29    }
30}
31
32// ── Registry ──────────────────────────────────────────────────────────────────
33
34#[derive(Clone)]
35pub struct MethodRegistry {
36    methods: IndexMap<String, Arc<dyn Method>>,
37}
38
39impl MethodRegistry {
40    pub fn new() -> Self {
41        Self { methods: IndexMap::new() }
42    }
43
44    /// Register a named method. Accepts anything that implements [`Method`],
45    /// including closures.
46    pub fn register(&mut self, name: impl Into<String>, method: impl Method + 'static) {
47        self.methods.insert(name.into(), Arc::new(method));
48    }
49
50    /// Look up a method by name.
51    #[inline]
52    pub fn get(&self, name: &str) -> Option<&Arc<dyn Method>> {
53        self.methods.get(name)
54    }
55
56    pub fn is_empty(&self) -> bool { self.methods.is_empty() }
57
58    /// Iterate every registered `(name, method)` pair.
59    pub fn iter(&self) -> impl Iterator<Item = (&str, &Arc<dyn Method>)> {
60        self.methods.iter().map(|(n, m)| (n.as_str(), m))
61    }
62
63    /// Register a method already wrapped in `Arc`. Lets callers share
64    /// a single method implementation across multiple registries.
65    pub fn register_arc(&mut self, name: impl Into<String>, method: Arc<dyn Method>) {
66        self.methods.insert(name.into(), method);
67    }
68}
69
70impl Default for MethodRegistry {
71    fn default() -> Self { Self::new() }
72}