1use std::collections::HashMap;
9use std::sync::Arc;
10
11use crate::runtime::Runtime;
12use crate::runtimes;
13
14#[derive(Clone, Default)]
16pub struct RuntimeRegistry {
17 by_lang: HashMap<String, Arc<dyn Runtime>>,
18}
19
20impl RuntimeRegistry {
21 pub fn new() -> Self {
23 Self {
24 by_lang: HashMap::new(),
25 }
26 }
27
28 pub fn with_builtins() -> Self {
37 let mut r = Self::new();
38 #[cfg(any(test, debug_assertions))]
39 r.register(runtimes::echo::EchoRuntime);
40 #[cfg(feature = "lang-lisp")]
41 r.register(runtimes::lisp::LispRuntime);
42 #[cfg(feature = "lang-js")]
43 r.register(runtimes::js::JsRuntime);
44 #[cfg(feature = "lang-python")]
45 r.register(runtimes::python::PythonRuntime);
46 #[cfg(feature = "lang-lua")]
47 r.register(runtimes::lua::LuaRuntime);
48 #[cfg(feature = "lang-rust")]
49 r.register(runtimes::rust::RustRuntime::default());
50 #[cfg(feature = "lang-query")]
51 r.register(runtimes::query::QueryRuntime);
52 r
53 }
54
55 pub fn register<R: Runtime + 'static>(&mut self, r: R) -> &mut Self {
58 self.by_lang
59 .insert(r.language().to_ascii_lowercase(), Arc::new(r));
60 self
61 }
62
63 pub fn get(&self, lang: &str) -> Option<Arc<dyn Runtime>> {
74 let key = outl_md::lang::canonical(lang)
75 .map(str::to_owned)
76 .unwrap_or_else(|| lang.to_ascii_lowercase());
77 self.by_lang.get(&key).cloned()
78 }
79
80 pub fn languages(&self) -> impl Iterator<Item = &str> {
82 self.by_lang.keys().map(String::as_str)
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn default_registers_echo() {
92 let r = RuntimeRegistry::with_builtins();
93 assert!(r.get("echo").is_some());
94 }
95
96 #[cfg(feature = "lang-lisp")]
97 #[test]
98 fn registers_lisp_when_feature_on() {
99 let r = RuntimeRegistry::with_builtins();
100 assert!(r.get("lisp").is_some());
101 }
102
103 #[test]
104 fn lookup_is_case_insensitive() {
105 let r = RuntimeRegistry::with_builtins();
106 assert!(r.get("ECHO").is_some());
107 assert!(r.get("Echo").is_some());
108 }
109
110 #[test]
111 fn unknown_language_returns_none() {
112 let r = RuntimeRegistry::with_builtins();
113 assert!(r.get("klingon").is_none());
114 }
115}