rhai/api/
build_type.rs

1//! Trait to build a custom type for use with [`Engine`].
2use crate::func::SendSync;
3use crate::module::FuncMetadata;
4use crate::packages::string_basic::{FUNC_TO_DEBUG, FUNC_TO_STRING};
5use crate::types::dynamic::Variant;
6use crate::{Engine, FuncRegistration, Identifier, RhaiNativeFunc, StaticVec};
7use std::marker::PhantomData;
8
9#[cfg(feature = "no_std")]
10use std::prelude::v1::*;
11
12#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
13use crate::func::register::Mut;
14
15/// Trait to build the API of a custom type for use with an [`Engine`]
16/// (i.e. register the type and its getters, setters, methods, etc.).
17///
18/// # Example
19///
20/// ```
21/// # #[cfg(not(feature = "no_object"))]
22/// # {
23/// use rhai::{CustomType, TypeBuilder, Engine};
24///
25/// #[derive(Debug, Clone, Eq, PartialEq)]
26/// struct TestStruct {
27///     field: i64
28/// }
29///
30/// impl TestStruct {
31///     fn new() -> Self {
32///         Self { field: 1 }
33///     }
34///     fn update(&mut self, offset: i64) {
35///         self.field += offset;
36///     }
37///     fn get_value(&mut self) -> i64 {
38///         self.field
39///     }
40///     fn set_value(&mut self, value: i64) {
41///        self.field = value;
42///     }
43/// }
44///
45/// impl CustomType for TestStruct {
46///     fn build(mut builder: TypeBuilder<Self>) {
47///         builder
48///             // Register pretty-print name of the type
49///             .with_name("TestStruct")
50///             // Register display functions
51///             .on_print(|v| format!("TestStruct({})", v.field))
52///             .on_debug(|v| format!("{v:?}"))
53///             // Register a constructor function
54///             .with_fn("new_ts", Self::new)
55///             // Register the 'update' method
56///             .with_fn("update", Self::update)
57///             // Register the 'value' property
58///             .with_get_set("value", Self::get_value, Self::set_value);
59///     }
60/// }
61///
62/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
63/// let mut engine = Engine::new();
64///
65/// // Register API for the custom type.
66/// engine.build_type::<TestStruct>();
67///
68/// assert_eq!(
69///     engine.eval::<TestStruct>("let x = new_ts(); x.update(41); print(x); x")?,
70///     TestStruct { field: 42 }
71/// );
72/// # Ok(())
73/// # }
74/// # }
75/// ```
76pub trait CustomType: Variant + Clone {
77    /// Builds the custom type for use with the [`Engine`].
78    ///
79    /// Methods, property getters/setters, indexers etc. should be registered in this function.
80    fn build(builder: TypeBuilder<Self>);
81}
82
83impl Engine {
84    /// Build the API of a custom type for use with the [`Engine`].
85    ///
86    /// The custom type must implement [`CustomType`].
87    #[inline]
88    pub fn build_type<T: CustomType>(&mut self) -> &mut Self {
89        T::build(TypeBuilder::new(self));
90        self
91    }
92}
93
94/// Builder to build the API of a custom type for use with an [`Engine`].
95///
96/// The type is automatically registered when this builder is dropped.
97///
98/// ## Pretty-Print Name
99///
100/// By default the type is registered with [`Engine::register_type`] (i.e. without a pretty-print name).
101///
102/// To define a pretty-print name, call [`with_name`][`TypeBuilder::with_name`],
103/// to use [`Engine::register_type_with_name`] instead.
104pub struct TypeBuilder<'a, T: Variant + Clone> {
105    engine: &'a mut Engine,
106    /// Keep the latest registered function(s) in cache to add additional metadata.
107    hashes: StaticVec<u64>,
108    _marker: PhantomData<T>,
109}
110
111impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
112    /// Create a [`TypeBuilder`] linked to a particular [`Engine`] instance.
113    #[inline(always)]
114    fn new(engine: &'a mut Engine) -> Self {
115        Self {
116            engine,
117            hashes: StaticVec::new_const(),
118            _marker: PhantomData,
119        }
120    }
121}
122
123impl<T: Variant + Clone> TypeBuilder<'_, T> {
124    /// Set a pretty-print name for the `type_of` function.
125    #[inline(always)]
126    pub fn with_name(&mut self, name: &str) -> &mut Self {
127        self.engine.register_type_with_name::<T>(name);
128        self
129    }
130
131    /// Set a comments for the type.
132    /// `TypeBuilder::with_name` must be called before this function, otherwise
133    /// the comments will not be registered.
134    ///
135    /// Available with the metadata feature only.
136    #[cfg(feature = "metadata")]
137    #[inline(always)]
138    pub fn with_comments(&mut self, comments: &[&str]) -> &mut Self {
139        let namespace = self.engine.global_namespace_mut();
140        if let Some(name) = namespace
141            .get_custom_type_raw::<T>()
142            .map(|ty| ty.display_name.clone())
143        {
144            namespace.set_custom_type_with_comments::<T>(name.as_str(), comments);
145        }
146
147        self
148    }
149
150    /// Pretty-print this custom type.
151    #[inline(always)]
152    pub fn on_print(
153        &mut self,
154        on_print: impl Fn(&mut T) -> String + SendSync + 'static,
155    ) -> &mut Self {
156        let FuncMetadata { hash, .. } =
157            FuncRegistration::new(FUNC_TO_STRING).register_into_engine(self.engine, on_print);
158        self.hashes.clear();
159        self.hashes.push(*hash);
160        self
161    }
162
163    /// Debug-print this custom type.
164    #[inline(always)]
165    pub fn on_debug(
166        &mut self,
167        on_debug: impl Fn(&mut T) -> String + SendSync + 'static,
168    ) -> &mut Self {
169        let FuncMetadata { hash, .. } =
170            FuncRegistration::new(FUNC_TO_DEBUG).register_into_engine(self.engine, on_debug);
171        self.hashes.clear();
172        self.hashes.push(*hash);
173        self
174    }
175
176    /// Register a custom method.
177    #[inline(always)]
178    pub fn with_fn<A: 'static, const N: usize, const X: bool, R: Variant + Clone, const F: bool>(
179        &mut self,
180        name: impl AsRef<str> + Into<Identifier>,
181        method: impl RhaiNativeFunc<A, N, X, R, F> + SendSync + 'static,
182    ) -> &mut Self {
183        let FuncMetadata { hash, .. } =
184            FuncRegistration::new(name).register_into_engine(self.engine, method);
185        self.hashes.clear();
186        self.hashes.push(*hash);
187        self
188    }
189
190    /// _(metadata)_ Add comments to the last registered function.
191    /// Available under the `metadata` feature only.
192    #[cfg(feature = "metadata")]
193    #[inline(always)]
194    pub fn and_comments(&mut self, comments: &[&str]) -> &mut Self {
195        let module = self.engine.global_namespace_mut();
196
197        for &hash in &self.hashes {
198            if let Some(f) = module.get_fn_metadata_mut(hash) {
199                f.comments = comments.into_iter().map(|&s| s.into()).collect();
200            }
201        }
202
203        self
204    }
205}
206
207impl<T> TypeBuilder<'_, T>
208where
209    T: Variant + Clone + IntoIterator,
210    <T as IntoIterator>::Item: Variant + Clone,
211{
212    /// Register a type iterator.
213    /// This is an advanced API.
214    #[inline(always)]
215    pub fn is_iterable(&mut self) -> &mut Self {
216        self.engine.register_iterator::<T>();
217        self
218    }
219}
220
221#[cfg(not(feature = "no_object"))]
222impl<T: Variant + Clone> TypeBuilder<'_, T> {
223    /// Register a getter function.
224    ///
225    /// The function signature must start with `&mut self` and not `&self`.
226    ///
227    /// Not available under `no_object`.
228    #[inline(always)]
229    pub fn with_get<const X: bool, R: Variant + Clone, const F: bool>(
230        &mut self,
231        name: impl AsRef<str>,
232        get_fn: impl RhaiNativeFunc<(Mut<T>,), 1, X, R, F> + SendSync + 'static,
233    ) -> &mut Self {
234        let FuncMetadata { hash, .. } =
235            FuncRegistration::new_getter(name).register_into_engine(self.engine, get_fn);
236        self.hashes.clear();
237        self.hashes.push(*hash);
238
239        self
240    }
241
242    /// Register a setter function.
243    ///
244    /// Not available under `no_object`.
245    #[inline(always)]
246    pub fn with_set<const X: bool, R: Variant + Clone, const F: bool>(
247        &mut self,
248        name: impl AsRef<str>,
249        set_fn: impl RhaiNativeFunc<(Mut<T>, R), 2, X, (), F> + SendSync + 'static,
250    ) -> &mut Self {
251        let FuncMetadata { hash, .. } =
252            FuncRegistration::new_setter(name).register_into_engine(self.engine, set_fn);
253        self.hashes.clear();
254        self.hashes.push(*hash);
255
256        self
257    }
258
259    /// Short-hand for registering both getter and setter functions.
260    ///
261    /// All function signatures must start with `&mut self` and not `&self`.
262    ///
263    /// Not available under `no_object`.
264    #[inline(always)]
265    pub fn with_get_set<
266        const X1: bool,
267        const X2: bool,
268        R: Variant + Clone,
269        const F1: bool,
270        const F2: bool,
271    >(
272        &mut self,
273        name: impl AsRef<str>,
274        get_fn: impl RhaiNativeFunc<(Mut<T>,), 1, X1, R, F1> + SendSync + 'static,
275        set_fn: impl RhaiNativeFunc<(Mut<T>, R), 2, X2, (), F2> + SendSync + 'static,
276    ) -> &mut Self {
277        let hash_1 = FuncRegistration::new_getter(&name)
278            .register_into_engine(self.engine, get_fn)
279            .hash;
280        let hash_2 = FuncRegistration::new_setter(&name)
281            .register_into_engine(self.engine, set_fn)
282            .hash;
283        self.hashes.clear();
284        self.hashes.push(hash_1);
285        self.hashes.push(hash_2);
286
287        self
288    }
289}
290
291#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
292impl<T: Variant + Clone> TypeBuilder<'_, T> {
293    /// Register an index getter.
294    ///
295    /// The function signature must start with `&mut self` and not `&self`.
296    ///
297    /// Not available under both `no_index` and `no_object`.
298    #[inline(always)]
299    pub fn with_indexer_get<
300        IDX: Variant + Clone,
301        const X: bool,
302        R: Variant + Clone,
303        const F: bool,
304    >(
305        &mut self,
306        get_fn: impl RhaiNativeFunc<(Mut<T>, IDX), 2, X, R, F> + SendSync + 'static,
307    ) -> &mut Self {
308        let FuncMetadata { hash, .. } =
309            FuncRegistration::new_index_getter().register_into_engine(self.engine, get_fn);
310        self.hashes.clear();
311        self.hashes.push(*hash);
312
313        self
314    }
315
316    /// Register an index setter.
317    ///
318    /// Not available under both `no_index` and `no_object`.
319    #[inline(always)]
320    pub fn with_indexer_set<
321        IDX: Variant + Clone,
322        const X: bool,
323        R: Variant + Clone,
324        const F: bool,
325    >(
326        &mut self,
327        set_fn: impl RhaiNativeFunc<(Mut<T>, IDX, R), 3, X, (), F> + SendSync + 'static,
328    ) -> &mut Self {
329        let FuncMetadata { hash, .. } =
330            FuncRegistration::new_index_setter().register_into_engine(self.engine, set_fn);
331        self.hashes.clear();
332        self.hashes.push(*hash);
333
334        self
335    }
336
337    /// Short-hand for registering both index getter and setter functions.
338    ///
339    /// Not available under both `no_index` and `no_object`.
340    #[inline(always)]
341    pub fn with_indexer_get_set<
342        IDX: Variant + Clone,
343        const X1: bool,
344        const X2: bool,
345        R: Variant + Clone,
346        const F1: bool,
347        const F2: bool,
348    >(
349        &mut self,
350        get_fn: impl RhaiNativeFunc<(Mut<T>, IDX), 2, X1, R, F1> + SendSync + 'static,
351        set_fn: impl RhaiNativeFunc<(Mut<T>, IDX, R), 3, X2, (), F2> + SendSync + 'static,
352    ) -> &mut Self {
353        let hash_1 = FuncRegistration::new_index_getter()
354            .register_into_engine(self.engine, get_fn)
355            .hash;
356        let hash_2 = FuncRegistration::new_index_setter()
357            .register_into_engine(self.engine, set_fn)
358            .hash;
359        self.hashes.clear();
360        self.hashes.push(hash_1);
361        self.hashes.push(hash_2);
362
363        self
364    }
365}