Skip to main content

ravenrs_live/
registry.rs

1//! What the interpreter can call in the host program: associated functions such as
2//! `Button::new`, methods such as `.style(..)`, constants such as `ButtonStyle::Ghost`, and
3//! conversions such as from a `Button` to an `AnyView`.
4
5use std::any::{Any, TypeId};
6use std::cell::RefCell;
7use std::collections::HashMap;
8use std::fmt;
9use std::rc::Rc;
10
11use crate::value::{FromLive, Host, IntoLive, Value};
12
13/// A function of the host program, taking the interpreter's values. For a method, the first
14/// value is the receiver.
15pub type HostFn = Rc<dyn Fn(Vec<Value>) -> Result<Value, String>>;
16
17type Convert = Rc<dyn Fn(Box<dyn Any>) -> Box<dyn Any>>;
18
19/// A method, with the Rust type its receiver holds when it is one of several of that name.
20type Method = (Option<TypeId>, HostFn);
21
22/// Reads a field of a host value.
23pub type FieldGet = Rc<dyn Fn(&Value) -> Result<Value, String>>;
24
25/// Changes a field of a host value in place.
26pub type FieldSet = Rc<dyn Fn(&Value, Value) -> Result<(), String>>;
27
28/// Builds a host struct from its fields, written as `Name { a: .., ..base }`.
29pub type RecordFn = Rc<dyn Fn(Vec<(String, Value)>, Option<Value>) -> Result<Value, String>>;
30
31/// Everything of the host program an interpreted program can reach.
32#[derive(Default)]
33pub struct Registry {
34    functions: HashMap<(String, String), HostFn>,
35    methods: HashMap<(String, String), Vec<Method>>,
36    constants: HashMap<(String, String), Rc<dyn Fn() -> Value>>,
37    free: HashMap<String, HostFn>,
38    conversions: HashMap<(TypeId, TypeId), Convert>,
39    aliases: HashMap<String, String>,
40    names: HashMap<TypeId, &'static str>,
41    fields: HashMap<(String, String), (Option<FieldGet>, Option<FieldSet>)>,
42    records: HashMap<String, RecordFn>,
43}
44
45impl fmt::Debug for Registry {
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        f.debug_struct("Registry")
48            .field("functions", &self.functions.len())
49            .field("methods", &self.methods.len())
50            .field("constants", &self.constants.len())
51            .finish_non_exhaustive()
52    }
53}
54
55impl Registry {
56    /// An empty registry.
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    /// `Type::name(..)`, taking the arguments as they are.
62    pub fn function(
63        &mut self,
64        ty: &str,
65        name: &str,
66        f: impl Fn(Vec<Value>) -> Result<Value, String> + 'static,
67    ) {
68        self.functions.insert((ty.to_owned(), name.to_owned()), Rc::new(f));
69    }
70
71    /// `value.name(..)` on a host value of `ty`; the receiver comes first.
72    pub fn method(
73        &mut self,
74        ty: &str,
75        name: &str,
76        f: impl Fn(Vec<Value>) -> Result<Value, String> + 'static,
77    ) {
78        self.methods.entry((ty.to_owned(), name.to_owned())).or_default().push((None, Rc::new(f)));
79    }
80
81    /// `value.name(..)` on a host value holding a `T`, for types that share a name, such as
82    /// the forms of a generic component.
83    pub fn method_for<T: 'static>(
84        &mut self,
85        ty: &str,
86        name: &str,
87        f: impl Fn(Vec<Value>) -> Result<Value, String> + 'static,
88    ) {
89        let list = self.methods.entry((ty.to_owned(), name.to_owned())).or_default();
90        list.retain(|(id, _)| *id != Some(TypeId::of::<T>()));
91        list.push((Some(TypeId::of::<T>()), Rc::new(f)));
92    }
93
94    /// `Type::NAME`, made each time it is read.
95    pub fn constant(&mut self, ty: &str, name: &str, make: impl Fn() -> Value + 'static) {
96        self.constants.insert((ty.to_owned(), name.to_owned()), Rc::new(make));
97    }
98
99    /// A function called by its name alone, such as a helper the samples share.
100    pub fn free_function(&mut self, name: &str, f: impl Fn(Vec<Value>) -> Result<Value, String> + 'static) {
101        self.free.insert(name.to_owned(), Rc::new(f));
102    }
103
104    /// How `value.name` reads a field of a host struct of `ty`.
105    pub fn field(&mut self, ty: &str, name: &str, get: impl Fn(&Value) -> Result<Value, String> + 'static) {
106        self.fields.entry((ty.to_owned(), name.to_owned())).or_default().0 = Some(Rc::new(get));
107    }
108
109    /// How `value.name = ..` changes a field of a host struct of `ty`.
110    pub fn set_field(
111        &mut self,
112        ty: &str,
113        name: &str,
114        set: impl Fn(&Value, Value) -> Result<(), String> + 'static,
115    ) {
116        self.fields.entry((ty.to_owned(), name.to_owned())).or_default().1 = Some(Rc::new(set));
117    }
118
119    /// How `Ty { a: .., b: .., ..base }` builds a host struct.
120    pub fn record(
121        &mut self,
122        ty: &str,
123        build: impl Fn(Vec<(String, Value)>, Option<Value>) -> Result<Value, String> + 'static,
124    ) {
125        self.records.insert(ty.to_owned(), Rc::new(build));
126    }
127
128    pub(crate) fn find_field(&self, ty: &str, name: &str) -> Option<FieldGet> {
129        self.fields.get(&(ty.to_owned(), name.to_owned()))?.0.clone()
130    }
131
132    pub(crate) fn find_set_field(&self, ty: &str, name: &str) -> Option<FieldSet> {
133        self.fields.get(&(ty.to_owned(), name.to_owned()))?.1.clone()
134    }
135
136    pub(crate) fn find_record(&self, ty: &str) -> Option<RecordFn> {
137        self.records.get(self.named(ty)).cloned()
138    }
139
140    /// Remembers which name a Rust type goes by, for conversions.
141    pub fn name_type<T: 'static>(&mut self, name: &'static str) {
142        self.names.insert(TypeId::of::<T>(), name);
143    }
144
145    /// How a host value of type `From` becomes a `To` when a function asks for a `To`, as a
146    /// `Button` becomes an `AnyView`.
147    pub fn conversion<From: 'static, To: 'static>(&mut self, name: &'static str, convert: fn(From) -> To) {
148        self.name_type::<From>(name);
149        let convert: Convert = Rc::new(move |any: Box<dyn Any>| -> Box<dyn Any> {
150            match any.downcast::<From>() {
151                Ok(value) => Box::new(convert(*value)),
152                Err(other) => other,
153            }
154        });
155        self.conversions.insert((TypeId::of::<From>(), TypeId::of::<To>()), convert);
156    }
157
158    /// Lets `alias` name the type registered as `ty`, as a crate re-exports `Stack as ChartStack`.
159    pub fn alias(&mut self, alias: &str, ty: &str) {
160        self.aliases.insert(alias.to_owned(), ty.to_owned());
161    }
162
163    fn named<'a>(&'a self, ty: &'a str) -> &'a str {
164        self.aliases.get(ty).map_or(ty, String::as_str)
165    }
166
167    pub(crate) fn find_function(&self, ty: &str, name: &str) -> Option<HostFn> {
168        self.functions.get(&(self.named(ty).to_owned(), name.to_owned())).cloned()
169    }
170
171    pub(crate) fn find_method(&self, ty: &str, name: &str) -> Option<HostFn> {
172        self.methods.get(&(ty.to_owned(), name.to_owned()))?.first().map(|(_, f)| f.clone())
173    }
174
175    /// The method for a receiver holding a value of `held`, or the first of that name.
176    pub(crate) fn find_method_for(&self, ty: &str, name: &str, held: Option<TypeId>) -> Option<HostFn> {
177        let list = self.methods.get(&(ty.to_owned(), name.to_owned()))?;
178        list.iter()
179            .find(|(id, _)| id.is_some() && *id == held)
180            .or_else(|| list.first())
181            .map(|(_, f)| f.clone())
182    }
183
184    pub(crate) fn find_constant(&self, ty: &str, name: &str) -> Option<Value> {
185        self.constants.get(&(self.named(ty).to_owned(), name.to_owned())).map(|make| make())
186    }
187
188    pub(crate) fn find_free(&self, name: &str) -> Option<HostFn> {
189        self.free.get(name).cloned()
190    }
191
192    /// Whether anything is known under this type name.
193    pub(crate) fn knows_type(&self, ty: &str) -> bool {
194        let ty = self.named(ty);
195        self.functions.keys().chain(self.constants.keys()).any(|(t, _)| t == ty)
196            || self.records.contains_key(ty)
197    }
198
199    fn convert(&self, held: TypeId, target: TypeId) -> Option<Convert> {
200        self.conversions.get(&(held, target)).cloned()
201    }
202}
203
204thread_local! {
205    static CURRENT: RefCell<Option<Rc<Registry>>> = const { RefCell::new(None) };
206}
207
208/// Makes `registry` the one conversions use on this thread while `f` runs, and after, for
209/// closures the host calls later.
210pub(crate) fn use_registry(registry: Rc<Registry>) {
211    CURRENT.with(|current| *current.borrow_mut() = Some(registry));
212}
213
214fn current() -> Option<Rc<Registry>> {
215    CURRENT.with(|current| current.borrow().clone())
216}
217
218impl Host {
219    /// Takes the value out as a `T`: the same type, or one the registry converts to `T`.
220    pub fn take_as<T: 'static>(&self) -> Result<T, String> {
221        if let Ok(value) = self.take::<T>() {
222            return Ok(value);
223        }
224        let convert = current()
225            .zip(self.held_type())
226            .and_then(|(registry, held)| registry.convert(held, TypeId::of::<T>()))
227            .ok_or_else(|| {
228                format!(
229                    "a {} cannot go where a {} goes",
230                    self.type_name,
231                    crate::value::short(std::any::type_name::<T>())
232                )
233            })?;
234        // A value that copies stays where it is, as `take` leaves it.
235        let boxed = match self.copy {
236            Some(copy) => {
237                let cell = self.cell.borrow();
238                let any = cell.as_ref().ok_or_else(|| format!("this {} was already used", self.type_name))?;
239                copy(any.as_ref())
240            }
241            None => self
242                .cell
243                .borrow_mut()
244                .take()
245                .ok_or_else(|| format!("this {} was already used", self.type_name))?,
246        };
247        convert(boxed)
248            .downcast::<T>()
249            .map(|b| *b)
250            .map_err(|_| format!("a {} did not convert", self.type_name))
251    }
252}
253
254/// A value the host can always fall back to when a closure it calls fails, so the failure is
255/// reported instead of stopping the program.
256pub trait Fallback {
257    /// The value to use instead.
258    fn fallback() -> Self;
259}
260
261macro_rules! defaults {
262    ($($ty:ty),*) => {$(impl Fallback for $ty { fn fallback() -> Self { <$ty>::default() } })*};
263}
264
265defaults!((), bool, i8, i16, i32, i64, u8, u16, u32, u64, usize, isize, f32, f64, char, String);
266
267impl<T> Fallback for Option<T> {
268    fn fallback() -> Self {
269        None
270    }
271}
272
273impl<T, E: Fallback> Fallback for Result<T, E> {
274    fn fallback() -> Self {
275        Err(E::fallback())
276    }
277}
278
279impl<T> Fallback for Vec<T> {
280    fn fallback() -> Self {
281        Vec::new()
282    }
283}
284
285macro_rules! callbacks {
286    ($(($($arg:ident),*)),*) => {$(
287        impl<$($arg: IntoLive + 'static,)* R: FromLive + Fallback + 'static> FromLive for Box<dyn Fn($($arg),*) -> R> {
288            #[allow(non_snake_case)]
289            fn from_live(value: Value) -> Result<Self, String> {
290                let Value::Closure(closure) = value.read() else {
291                    return Err(format!("expected a closure, found {}", value.kind()));
292                };
293                Ok(Box::new(move |$($arg: $arg),*| {
294                    let arguments = vec![$($arg.into_live()),*];
295                    match closure.call(arguments).and_then(|v| R::from_live(v).map_err(|e| closure.error(e))) {
296                        Ok(result) => result,
297                        Err(problem) => {
298                            crate::value::report(problem);
299                            R::fallback()
300                        }
301                    }
302                }))
303            }
304        }
305        impl<$($arg: IntoLive + 'static,)* R: FromLive + Fallback + 'static> FromLive for Rc<dyn Fn($($arg),*) -> R> {
306            fn from_live(value: Value) -> Result<Self, String> {
307                Box::<dyn Fn($($arg),*) -> R>::from_live(value).map(Rc::from)
308            }
309        }
310    )*};
311}
312
313callbacks!((), (A), (A, B), (A, B, C));
314
315impl<T: FromLive + IntoLive + Clone + 'static> FromLive for ravenrs_reactive::Binding<T> {
316    fn from_live(value: Value) -> Result<Self, String> {
317        let Some(state) = value.as_binding() else {
318            return Err(format!("expected a State or its binding, found {}", value.kind()));
319        };
320        let first = T::from_live(state.get())?;
321        let last = Rc::new(RefCell::new(first));
322        let (read, write, kept) = (state.clone(), state, last.clone());
323        Ok(ravenrs_reactive::Binding::new(
324            move || match T::from_live(read.get()) {
325                Ok(value) => {
326                    *last.borrow_mut() = value.clone();
327                    value
328                }
329                Err(_) => last.borrow().clone(),
330            },
331            move |value: T| {
332                *kept.borrow_mut() = value.clone();
333                write.set(value.into_live());
334            },
335        ))
336    }
337}