1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//! Plugin system for extending ccalc with new built-in functions.
//!
//! Third-party crates implement the [`plugin::Plugin`] trait and register via
//! [`plugin::register_plugin`]. The engine checks the registry before its own
//! built-in table, so plugins can shadow any existing built-in if needed.
//!
//! # Minimal plugin example
//!
//! ```rust,ignore
//! use ccalc_engine::env::{Env, Value};
//! use ccalc_engine::plugin::Plugin;
//!
//! pub struct MyPlugin;
//!
//! impl Plugin for MyPlugin {
//! fn name(&self) -> &str { "myfunc" }
//!
//! fn call(&self, _name: &str, args: &[Value], _env: &Env) -> Result<Value, String> {
//! if args.is_empty() {
//! return Err("myfunc: at least one argument required".into());
//! }
//! Ok(args[0].clone())
//! }
//! }
//!
//! // In main.rs / startup:
//! ccalc_engine::plugin::register_plugin(Box::new(MyPlugin));
//! ```
//!
//! Plugins that expose several names (e.g. a plot plugin with `plot`, `scatter`,
//! `bar`, …) should also override [`plugin::Plugin::exported_names`] and return a
//! `const`-backed slice. The `name` argument to `call` identifies which exported
//! name was invoked, enabling a single plugin to dispatch multiple functions:
//!
//! ```rust,ignore
//! const NAMES: &[&str] = &["plot", "scatter", "bar"];
//!
//! fn exported_names(&self) -> &[&str] { NAMES }
//!
//! fn call(&self, name: &str, args: &[Value], _env: &Env) -> Result<Value, String> {
//! match name {
//! "plot" => { /* ... */ Ok(Value::Void) }
//! "scatter" => { /* ... */ Ok(Value::Void) }
//! _ => Err(format!("{name}: not implemented"))
//! }
//! }
//! ```
use RefCell;
use crate;
/// Trait that all ccalc plugins must implement.
///
/// Implement this in a separate crate and register an instance via
/// [`register_plugin`] before any evaluation takes place.
/// Registry that maps exported names to their plugin implementations.
thread_local!
/// Registers a plugin in the thread-local plugin registry.
///
/// Call this once at program startup (before any evaluation) for each plugin.
///
/// # Examples
///
/// ```rust,ignore
/// ccalc_engine::plugin::register_plugin(Box::new(MyPlugin));
/// ```
/// Calls the plugin that handles `name`, if one is registered.
///
/// Returns `Some(result)` when a plugin claims the name, `None` otherwise.
/// Used by `call_builtin` to dispatch before the built-in table.
pub
/// Returns all names exported by currently registered plugins.
///
/// Used for tab completion in the REPL.