1use super::builtins::BuiltinKind;
4use super::erased::{Erased, ErasedFunction};
5use super::templated::{default_problem, primary_problem};
6use super::{CallLayer, Function, FunctionConfig, FunctionSpec};
7use crate::base::{AuthoredPath, Error, ErrorKind, PathSeg};
8use crate::expr::{ExprEngine, FieldKind, FieldSpec, ValueKind};
9use std::collections::BTreeMap;
10use std::fmt;
11use std::str::FromStr;
12use std::sync::Arc;
13
14#[derive(Clone)]
39pub struct Registry {
40 inner: Arc<Inner>,
41}
42
43#[derive(Clone)]
44struct Inner {
45 exprs: ExprEngine,
46 builtin_specs: [FunctionSpec; BuiltinKind::ALL.len()],
48 hosts: BTreeMap<Box<str>, Arc<dyn ErasedFunction>>,
49 layers: Vec<Arc<dyn CallLayer>>,
51}
52
53#[derive(Clone, Copy)]
55pub(crate) enum Entry<'r> {
56 Builtin(BuiltinKind),
58 Host(&'r dyn ErasedFunction),
60}
61
62impl Registry {
63 pub fn new(exprs: ExprEngine) -> Self {
65 Self {
66 inner: Arc::new(Inner {
67 exprs,
68 builtin_specs: BuiltinKind::ALL.map(BuiltinKind::spec),
69 hosts: BTreeMap::new(),
70 layers: Vec::new(),
71 }),
72 }
73 }
74
75 pub fn register<F: Function>(&mut self, name: &str, f: F) {
114 if let Err(error) = self.try_register(name, f) {
115 panic!("{}", error.message());
116 }
117 }
118
119 pub fn try_register<F: Function>(&mut self, name: &str, f: F) -> Result<(), Error> {
128 if crate::def::is_reserved_function(name) {
129 return Err(Error::new(
130 ErrorKind::Config,
131 format!(
132 "cannot register `{name}`: the name is reserved for the built-in functions \
133 (def::BUILTIN_FUNCTIONS, and v3's alias for `validation`)"
134 ),
135 ));
136 }
137 let erased = Erased::new(f);
138 let templated = <F::Config as FunctionConfig>::TEMPLATED;
139 check_spec(
140 name,
141 erased.spec(),
142 erased.fields(),
143 templated,
144 &self.inner.exprs,
145 )
146 .map_err(|message| Error::new(ErrorKind::Config, message))?;
147 let erased: Arc<dyn ErasedFunction> = Arc::new(erased);
148 Arc::make_mut(&mut self.inner)
149 .hosts
150 .insert(Box::from(name), erased);
151 Ok(())
152 }
153
154 pub fn layer<L: CallLayer>(&mut self, layer: L) {
157 Arc::make_mut(&mut self.inner).layers.push(Arc::new(layer));
158 }
159
160 pub(crate) fn ptr_eq(&self, other: &Self) -> bool {
163 Arc::ptr_eq(&self.inner, &other.inner)
164 }
165
166 pub fn exprs(&self) -> &ExprEngine {
168 &self.inner.exprs
169 }
170
171 pub fn catalogue(&self) -> impl Iterator<Item = FunctionInfo<'_>> {
175 let builtins = BuiltinKind::ALL
176 .into_iter()
177 .zip(&self.inner.builtin_specs)
178 .map(|(kind, spec)| FunctionInfo {
179 name: kind.name(),
180 builtin: true,
181 spec,
182 fields: kind.fields(),
183 });
184 let hosts = self.inner.hosts.iter().map(|(name, f)| FunctionInfo {
185 name,
186 builtin: false,
187 spec: f.spec(),
188 fields: f.fields(),
189 });
190 builtins.chain(hosts)
191 }
192
193 pub(crate) fn entry(&self, name: &str) -> Option<Entry<'_>> {
196 match BuiltinKind::from_name(name) {
197 Some(kind) => Some(Entry::Builtin(kind)),
198 None => self.inner.hosts.get(name).map(|f| Entry::Host(&**f)),
199 }
200 }
201
202 pub(crate) fn layers(&self) -> &[Arc<dyn CallLayer>] {
204 &self.inner.layers
205 }
206}
207
208fn check_spec(
213 name: &str,
214 spec: &FunctionSpec,
215 fields: &[FieldSpec],
216 templated: bool,
217 exprs: &ExprEngine,
218) -> Result<(), String> {
219 match (templated, spec.runtime_fields.is_some()) {
220 (false, true) => {
221 return Err(format!(
222 "cannot register `{name}`: its spec declares runtime fields, but its config is \
223 not `Templated`, so it never compiles them"
224 ));
225 }
226 (true, false) => {
227 return Err(format!(
228 "cannot register `{name}`: its config is `Templated`, but its spec declares no \
229 runtime fields, so every compile of it fails"
230 ));
231 }
232 _ => {}
233 }
234 if let Some(problem) = spec.runtime_fields.as_deref().and_then(primary_problem) {
235 return Err(format!("cannot register `{name}`: {problem}"));
236 }
237 for (index, field) in fields.iter().enumerate() {
238 if fields[..index].iter().any(|other| other.name == field.name) {
239 return Err(format!(
240 "cannot register `{name}`: its field `{}` is declared twice",
241 field.name
242 ));
243 }
244 if templated
245 && matches!(
246 field.kind,
247 FieldKind::Nested(_)
248 | FieldKind::List(_)
249 | FieldKind::Map(_)
250 | FieldKind::Templated
251 )
252 {
253 return Err(format!(
254 "cannot register `{name}`: its runtime field `{}` has an unsupported kind; \
255 use an expression, document, literal or output",
256 field.name
257 ));
258 }
259 if let Some(problem) = default_problem(exprs, field) {
260 return Err(format!("cannot register `{name}`: {problem}"));
261 }
262 }
263 for reference in &spec.references {
264 let field = &reference.field;
265 let path = AuthoredPath::from_str(field).map_err(|error| {
266 format!(
267 "cannot register `{name}`: its reference field `{field}` is not an authored \
268 path ({error})"
269 )
270 })?;
271 if let Err(why) = reaches(fields, path.segs()) {
272 return Err(format!(
273 "cannot register `{name}`: its reference field `{field}` {why}"
274 ));
275 }
276 }
277 Ok(())
278}
279
280fn reaches(fields: &[FieldSpec], segs: &[PathSeg]) -> Result<(), String> {
289 let start = || "does not start at a field of its config".to_owned();
290 let Some((PathSeg::Key(first), rest)) = segs.split_first() else {
291 return Err(start());
292 };
293 let Some(spec) = fields.iter().find(|f| f.name == **first) else {
294 return Err(start());
295 };
296 let mut kind = spec.kind;
297 let mut at = AuthoredPath::root().join(&**first);
298 for seg in rest {
299 let refused = |why: String| {
300 Err(format!(
301 "does not reach a field of its config: `{at}` {why}"
302 ))
303 };
304 kind = match (kind, seg) {
305 (
306 FieldKind::Literal(ValueKind::Any | ValueKind::Object | ValueKind::Array)
307 | FieldKind::Interpolated
308 | FieldKind::Templated,
309 _,
310 ) => return Ok(()),
311 (FieldKind::Nested(fields), PathSeg::Key(key)) => {
312 match fields().iter().find(|f| f.name == **key) {
313 Some(spec) => spec.kind,
314 None => return refused(format!("has no field `{key}`")),
315 }
316 }
317 (FieldKind::List(element), PathSeg::Index(_)) => element(),
318 (FieldKind::Map(element), PathSeg::Key(_)) => element(),
319 (FieldKind::List(_), PathSeg::Key(key)) => {
320 return refused(format!("is a list, so `{key}` is not an index"));
321 }
322 (_, PathSeg::Index(index)) => {
323 return refused(format!("is not a list, so it has no `[{index}]`"));
324 }
325 (_, PathSeg::Key(key)) => {
326 return refused(format!("holds no fields, so it has no `{key}`"));
327 }
328 };
329 at.push(seg.clone());
330 }
331 Ok(())
332}
333
334impl Default for Registry {
335 fn default() -> Self {
337 Self::new(ExprEngine::default())
338 }
339}
340
341impl fmt::Debug for Registry {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 let names: Vec<&str> = self.catalogue().map(|info| info.name).collect();
345 f.debug_struct("Registry")
346 .field("functions", &names)
347 .finish_non_exhaustive()
348 }
349}
350
351#[derive(Clone, Copy, Debug)]
353#[non_exhaustive]
354pub struct FunctionInfo<'a> {
355 pub name: &'a str,
357 pub builtin: bool,
359 pub spec: &'a FunctionSpec,
361 pub fields: &'a [FieldSpec],
363}
364
365#[cfg(test)]
366mod tests {
367 use super::*;
368 use crate::base::Result;
369 use crate::def::BUILTIN_FUNCTIONS;
370 use crate::function::{BoxFuture, Ctx, Next, Outcome};
371
372 struct Ping;
373
374 impl Function for Ping {
375 type Config = ();
376 fn spec(&self) -> FunctionSpec {
377 FunctionSpec::default().with_description("ping")
378 }
379 async fn call(&self, _ctx: &mut Ctx<'_>, _cfg: &()) -> Result<Outcome> {
380 Ok(Outcome::Ok)
381 }
382 }
383
384 struct Layer;
385
386 impl CallLayer for Layer {
387 fn call<'a>(
388 &'a self,
389 ctx: &'a mut Ctx<'_>,
390 next: Next<'a>,
391 ) -> BoxFuture<'a, Result<Outcome>> {
392 next.run(ctx)
393 }
394 }
395
396 #[test]
397 fn a_builtin_name_resolves_to_its_kind() {
398 let registry = Registry::default();
399 for name in BUILTIN_FUNCTIONS {
400 match registry.entry(name) {
401 Some(Entry::Builtin(kind)) => assert_eq!(kind.name(), name),
402 _ => panic!("`{name}` is a built-in"),
403 }
404 }
405 assert!(registry.entry("validate").is_none());
407 assert!(registry.entry("ping").is_none());
408 }
409
410 #[test]
411 fn a_host_name_resolves_to_its_function() {
412 let mut registry = Registry::default();
413 registry.register("ping", Ping);
414 match registry.entry("ping") {
415 Some(Entry::Host(f)) => assert_eq!(f.spec().description, "ping"),
416 _ => panic!("`ping` is registered"),
417 }
418 }
419
420 #[test]
421 fn the_builtin_specs_are_computed_in_catalogue_order() {
422 let registry = Registry::default();
423 for (kind, info) in BuiltinKind::ALL.into_iter().zip(registry.catalogue()) {
424 assert_eq!(info.name, kind.name());
425 assert_eq!(info.spec.description, kind.spec().description);
426 assert_eq!(info.spec.category, kind.spec().category);
427 }
428 }
429
430 #[test]
431 fn a_clone_shares_the_table_until_it_is_written() {
432 let mut registry = Registry::default();
433 registry.layer(Layer);
434 let mut clone = registry.clone();
435 assert!(Arc::ptr_eq(&clone.inner, ®istry.inner));
436 clone.layer(Layer);
437 assert!(!Arc::ptr_eq(&clone.inner, ®istry.inner));
438 assert_eq!(registry.layers().len(), 1);
439 assert_eq!(clone.layers().len(), 2);
440 }
441}