script_format/
engine.rs

1use std::any::TypeId;
2use std::cell::RefCell;
3use std::iter::once;
4use std::ops::Deref;
5use std::ops::DerefMut;
6use std::path::Path;
7use std::rc::Rc;
8
9use rhai::CustomType;
10use rhai::{
11    packages::{CorePackage, Package},
12    Dynamic, Engine, EvalAltResult, ImmutableString, Scope, Variant, FLOAT, INT,
13};
14
15use crate::internal::ToBe;
16
17/// A type alias for the result of script execution within the `FormattingEngine`.
18///
19/// This alias simplifies error handling when executing Rhai scripts, encapsulating
20/// either a successful result (`T`) or an error (`Box<EvalAltResult>`).
21///
22/// # Examples
23///
24/// ```rust
25/// use script_format::{
26///     FormattingEngine,
27///     ScriptResult,
28/// };
29///
30/// fn execute_script(script: &str) -> ScriptResult<()> {
31///     let mut engine = FormattingEngine::new(false);
32///     engine.format("data", 42, script)?;
33///     Ok(())
34/// }
35/// ```
36///
37/// # Type Parameters
38///
39/// * `T` - The type of the successful result.
40pub type ScriptResult<T> = Result<T, Box<EvalAltResult>>;
41
42/// A wrapper around the Rhai `Engine` for formatting data using a dsl based on rhai.
43///
44/// `FormattingEngine` allows you to register custom types and format them using a custom dsl based on rhai.
45///
46/// # Examples
47///
48/// ```rust
49/// use script_format::FormattingEngine;
50///
51/// let mut engine = FormattingEngine::new(false);
52/// let result = engine.format("name", "World", "~ `Hello, ${name}!`");
53/// assert_eq!(result.unwrap(), "Hello, World!");
54/// ```
55///
56/// # Features
57///
58/// - Custom type registration
59/// - Script execution with data binding
60/// - Debug support for script evaluation
61pub struct FormattingEngine {
62    engine: Engine,
63    messages: Rc<RefCell<Vec<String>>>,
64}
65
66impl Deref for FormattingEngine {
67    type Target = Engine;
68
69    fn deref(&self) -> &Self::Target {
70        &self.engine
71    }
72}
73
74impl DerefMut for FormattingEngine {
75    fn deref_mut(&mut self) -> &mut Self::Target {
76        &mut self.engine
77    }
78}
79
80#[inline(always)]
81fn minus_deprecation() {
82    eprintln!(
83        "Using '-' for printing is deprecated and will be removed in future versions. Use '~' instead."
84    )
85}
86
87impl FormattingEngine {
88    fn register_value<T: Variant + Clone + std::fmt::Display>(&mut self) {
89        self.engine
90            .register_fn("++", move |a: T, b: serde_value::Value| {
91                vec![a.to_string(), serde_json::to_string(&b).unwrap()]
92            });
93        self.engine
94            .register_fn("++", move |a: serde_value::Value, b: T| {
95                vec![serde_json::to_string(&a).unwrap(), b.to_string()]
96            });
97    }
98
99    fn register_string_concat_void<T: Variant + Clone + std::fmt::Display>(&mut self) {
100        self.engine
101            .register_fn("++", move |a: T, _b: ()| vec![a.to_string()]);
102        self.engine
103            .register_fn("++", move |_a: (), b: T| vec![b.to_string()]);
104    }
105
106    fn register_string_concat<T: Variant + Clone + std::fmt::Display>(&mut self) {
107        self.engine.register_fn("++", move |a: T, b: &str| {
108            vec![a.to_string(), b.to_string()]
109        });
110        self.engine.register_fn("++", move |a: &str, b: T| {
111            vec![a.to_string(), b.to_string()]
112        });
113        self.engine
114            .register_fn("++", move |a: T, b: T| vec![a.to_string(), b.to_string()]);
115    }
116
117    fn register_string_concat_vec<T: Variant + Clone + std::fmt::Display>(&mut self) {
118        self.engine.register_fn("++", move |a: Vec<T>, b: &str| {
119            a.iter()
120                .map(ToString::to_string)
121                .chain(once(b.to_owned()))
122                .collect::<Vec<_>>()
123        });
124        self.engine.register_fn("++", move |a: &str, b: Vec<T>| {
125            b.iter()
126                .map(ToString::to_string)
127                .chain(once(a.to_owned()))
128                .collect::<Vec<_>>()
129        });
130        self.engine.register_fn("++", move |a: Vec<T>, b: Vec<T>| {
131            a.iter()
132                .map(ToString::to_string)
133                .chain(b.iter().map(ToString::to_string))
134                .collect::<Vec<_>>()
135        });
136    }
137
138    fn register_concat<T: Variant + Clone + std::fmt::Display>(&mut self) {
139        self.register_string_concat::<T>();
140        self.register_string_concat_vec::<T>();
141        self.register_string_concat_void::<T>();
142    }
143
144    fn register_msg<T: Variant + Clone + std::fmt::Display>(&mut self) {
145        self.register_msg_single::<T>();
146        self.register_msg_multi::<T, &str>();
147        self.register_msg_multi::<T, String>();
148        self.register_msg_multi::<T, bool>();
149        self.register_msg_multi::<T, i64>();
150        self.register_msg_multi::<T, u64>();
151        self.register_msg_multi::<T, i32>();
152        self.register_msg_multi::<T, u32>();
153        self.register_msg_multi::<T, i16>();
154        self.register_msg_multi::<T, u16>();
155        self.register_msg_multi::<T, i8>();
156        self.register_msg_multi::<T, u8>();
157        self.register_msg_multi::<T, usize>();
158        self.register_msg_multi::<T, isize>();
159        self.register_msg_multi::<T, i128>();
160        self.register_msg_multi::<T, u128>();
161        self.register_msg_multi::<T, f32>();
162        self.register_msg_multi::<T, f64>();
163    }
164
165    fn register_msg_multi<
166        A: Variant + Clone + std::fmt::Display,
167        B: Variant + Clone + std::fmt::Display,
168    >(
169        &mut self,
170    ) {
171        self.engine
172            .register_fn("++", move |a: A, b: B| vec![a.to_string(), b.to_string()]);
173
174        self.engine
175            .register_fn("++", move |b: B, a: A| vec![b.to_string(), a.to_string()]);
176
177        self.engine.register_fn("++", move |a: Option<A>, b: B| {
178            if let Some(a) = a {
179                vec![a.to_string(), b.to_string()]
180            } else {
181                vec![b.to_string()]
182            }
183        });
184
185        self.engine.register_fn("++", move |a: A, b: Option<B>| {
186            if let Some(b) = b {
187                vec![a.to_string(), b.to_string()]
188            } else {
189                vec![a.to_string()]
190            }
191        });
192
193        self.engine
194            .register_fn("++", move |a: Option<A>, b: Option<B>| match (a, b) {
195                (Some(a), Some(b)) => vec![a.to_string(), b.to_string()],
196                (Some(a), None) => vec![a.to_string()],
197                (None, Some(b)) => vec![b.to_string()],
198                (None, None) => vec![],
199            });
200    }
201
202    fn register_msg_single<T: Variant + Clone + std::fmt::Display>(&mut self) {
203        {
204            let messages = self.clone_messages();
205            self.engine.register_fn("-", move |msg: T| {
206                minus_deprecation();
207                messages.borrow_mut().push(msg.to_string());
208            });
209        }
210
211        {
212            let messages = self.clone_messages();
213            self.engine.register_fn("-", move |msg: Option<T>| {
214                minus_deprecation();
215                if let Some(msg) = msg {
216                    messages.borrow_mut().push(msg.to_string());
217                }
218            });
219        }
220    }
221
222    fn register_vec<T: Variant + Clone>(&mut self) {
223        self.engine
224            .register_type::<Vec<T>>()
225            .register_fn("len", |v: Vec<T>| v.len())
226            .register_iterator::<Vec<T>>()
227            .register_iterator::<&Vec<&T>>()
228            .register_iterator::<Vec<T>>()
229            .register_iterator::<&Vec<&T>>()
230            .register_indexer_get(|v: &mut Vec<T>, i: i64| {
231                v[usize::try_from(i).unwrap()].to_owned()
232            });
233    }
234
235    fn register_vec_printable<T: Variant + Clone + std::fmt::Display>(&mut self) {
236        self.engine.register_fn("++", move |a: Vec<T>| {
237            a.iter().map(ToString::to_string).collect::<Vec<_>>()
238        });
239
240        self.register_vec_printable_single::<T, &str>();
241        self.register_vec_printable_single::<T, String>();
242        self.register_vec_printable_single::<T, bool>();
243        self.register_vec_printable_single::<T, i64>();
244        self.register_vec_printable_single::<T, u64>();
245        self.register_vec_printable_single::<T, i32>();
246        self.register_vec_printable_single::<T, u32>();
247        self.register_vec_printable_single::<T, i16>();
248        self.register_vec_printable_single::<T, u16>();
249        self.register_vec_printable_single::<T, i8>();
250        self.register_vec_printable_single::<T, u8>();
251        self.register_vec_printable_single::<T, usize>();
252        self.register_vec_printable_single::<T, isize>();
253        self.register_vec_printable_single::<T, i128>();
254        self.register_vec_printable_single::<T, u128>();
255        self.register_vec_printable_single::<T, f32>();
256        self.register_vec_printable_single::<T, f64>();
257
258        self.register_vec_printable_multi::<T, &str>();
259        self.register_vec_printable_multi::<T, String>();
260        self.register_vec_printable_multi::<T, bool>();
261        self.register_vec_printable_multi::<T, i64>();
262        self.register_vec_printable_multi::<T, u64>();
263        self.register_vec_printable_multi::<T, i32>();
264        self.register_vec_printable_multi::<T, u32>();
265        self.register_vec_printable_multi::<T, i16>();
266        self.register_vec_printable_multi::<T, u16>();
267        self.register_vec_printable_multi::<T, i8>();
268        self.register_vec_printable_multi::<T, u8>();
269        self.register_vec_printable_multi::<T, usize>();
270        self.register_vec_printable_multi::<T, isize>();
271        self.register_vec_printable_multi::<T, i128>();
272        self.register_vec_printable_multi::<T, u128>();
273        self.register_vec_printable_multi::<T, f32>();
274        self.register_vec_printable_multi::<T, f64>();
275
276        self.register_vec_printable_void::<&str>();
277        self.register_vec_printable_void::<String>();
278        self.register_vec_printable_void::<bool>();
279        self.register_vec_printable_void::<i64>();
280        self.register_vec_printable_void::<u64>();
281        self.register_vec_printable_void::<i32>();
282        self.register_vec_printable_void::<u32>();
283        self.register_vec_printable_void::<i16>();
284        self.register_vec_printable_void::<u16>();
285        self.register_vec_printable_void::<i8>();
286        self.register_vec_printable_void::<u8>();
287        self.register_vec_printable_void::<usize>();
288        self.register_vec_printable_void::<isize>();
289        self.register_vec_printable_void::<i128>();
290        self.register_vec_printable_void::<u128>();
291        self.register_vec_printable_void::<f32>();
292        self.register_vec_printable_void::<f64>();
293    }
294
295    fn register_vec_printable_void<T: Variant + Clone + std::fmt::Display>(&mut self) {
296        self.engine.register_fn("++", move |a: Vec<T>, _b: ()| {
297            a.iter().map(ToString::to_string).collect::<Vec<_>>()
298        });
299        self.engine.register_fn("++", move |_a: (), b: Vec<T>| {
300            b.iter().map(ToString::to_string).collect::<Vec<_>>()
301        });
302        self.engine
303            .register_fn("++", move |a: Option<Vec<T>>, _b: ()| {
304                a.unwrap_or_default()
305                    .iter()
306                    .map(ToString::to_string)
307                    .collect::<Vec<_>>()
308            });
309        self.engine
310            .register_fn("++", move |_a: (), b: Option<Vec<T>>| {
311                b.unwrap_or_default()
312                    .iter()
313                    .map(ToString::to_string)
314                    .collect::<Vec<_>>()
315            });
316    }
317
318    fn register_vec_printable_multi<
319        A: Variant + Clone + std::fmt::Display,
320        B: Variant + Clone + std::fmt::Display,
321    >(
322        &mut self,
323    ) {
324        self.engine.register_fn("++", move |a: Vec<A>, b: Vec<B>| {
325            a.iter()
326                .map(ToString::to_string)
327                .chain(b.iter().map(ToString::to_string))
328                .collect::<Vec<_>>()
329        });
330
331        self.engine
332            .register_fn("++", move |a: Option<Vec<A>>, b: Vec<B>| {
333                let a: Vec<A> = a.unwrap_or_default();
334                a.iter()
335                    .map(ToString::to_string)
336                    .chain(b.iter().map(ToString::to_string))
337                    .collect::<Vec<_>>()
338            });
339        self.engine
340            .register_fn("++", move |a: Vec<A>, b: Option<Vec<B>>| {
341                let b: Vec<B> = b.unwrap_or_default();
342                a.iter()
343                    .map(ToString::to_string)
344                    .chain(b.iter().map(ToString::to_string))
345                    .collect::<Vec<_>>()
346            });
347    }
348
349    fn register_vec_printable_single<
350        A: Variant + Clone + std::fmt::Display,
351        B: Variant + Clone + std::fmt::Display,
352    >(
353        &mut self,
354    ) {
355        self.engine.register_fn("++", move |a: Vec<A>, b: B| {
356            a.iter()
357                .map(ToString::to_string)
358                .chain(once(b.to_string()))
359                .collect::<Vec<_>>()
360        });
361        self.engine.register_fn("++", move |a: A, b: Vec<B>| {
362            once(a.to_string())
363                .chain(b.iter().map(ToString::to_string))
364                .collect::<Vec<_>>()
365        });
366
367        self.engine
368            .register_fn("++", move |a: Option<Vec<A>>, b: B| {
369                let a: Vec<A> = a.unwrap_or_default();
370                a.iter()
371                    .map(ToString::to_string)
372                    .chain(once(b.to_string()))
373                    .collect::<Vec<_>>()
374            });
375        self.engine
376            .register_fn("++", move |a: A, b: Option<Vec<B>>| {
377                let b: Vec<B> = b.unwrap_or_default();
378                once(a.to_string())
379                    .chain(b.iter().map(ToString::to_string))
380                    .collect::<Vec<_>>()
381            });
382
383        self.engine
384            .register_fn("++", move |a: Vec<A>, b: Option<B>| {
385                let mut res = a.iter().map(ToString::to_string).collect::<Vec<_>>();
386                if let Some(b) = b {
387                    res.push(b.to_string());
388                }
389                res
390            });
391        self.engine
392            .register_fn("++", move |a: Option<A>, b: Vec<B>| {
393                a.iter()
394                    .map(ToString::to_string)
395                    .chain(b.iter().map(ToString::to_string))
396                    .collect::<Vec<_>>()
397            });
398    }
399
400    fn register_type_dynamic<T: Variant + Clone + 'static, C: From<T> + PartialEq + 'static>(
401        &mut self,
402        is_call: fn(&Dynamic) -> bool,
403        as_call: fn(&Dynamic) -> Result<C, &'static str>,
404    ) {
405        self.engine
406            .register_fn("any", move |arr: rhai::Array, v: T| {
407                let value: C = v.into();
408                arr.iter()
409                    .filter(|a| is_call(a))
410                    .map(|a| {
411                        let a: C = as_call(&a).unwrap().into();
412                        a
413                    })
414                    .filter(|a| *a == value)
415                    .into_iter()
416                    .count()
417                    > 0
418            });
419        self.engine
420            .register_fn("all", move |arr: rhai::Array, v: T| {
421                let value: C = v.into();
422                let expected = arr.len();
423                arr.iter()
424                    .filter(|a| is_call(a))
425                    .map(|a| {
426                        let a: C = as_call(&a).unwrap().into();
427                        a
428                    })
429                    .filter(|a| *a == value)
430                    .into_iter()
431                    .count()
432                    == expected
433            });
434        self.engine
435            .register_fn("none", move |arr: rhai::Array, v: T| {
436                let value: C = v.into();
437                arr.iter()
438                    .filter(|a| is_call(a))
439                    .map(|a| {
440                        let a: C = as_call(&a).unwrap().into();
441                        a
442                    })
443                    .filter(|a| *a == value)
444                    .into_iter()
445                    .count()
446                    == 0
447            });
448    }
449
450    fn register_comparison<
451        A: Variant + Clone + AsCast<C>,
452        B: Variant + Clone + AsCast<C>,
453        C: PartialEq + PartialOrd,
454    >(
455        &mut self,
456    ) {
457        self.engine
458            .register_fn(">", |left: A, right: B| left.as_cast() > right.as_cast());
459        self.engine
460            .register_fn(">=", |left: A, right: B| left.as_cast() >= right.as_cast());
461        self.engine
462            .register_fn("<", |left: A, right: B| left.as_cast() < right.as_cast());
463        self.engine
464            .register_fn("<=", |left: A, right: B| left.as_cast() <= right.as_cast());
465        self.engine
466            .register_fn("!=", |left: A, right: B| left.as_cast() != right.as_cast());
467        self.engine
468            .register_fn("==", |left: A, right: B| left.as_cast() == right.as_cast());
469
470        self.engine
471            .register_fn(">", |left: B, right: A| left.as_cast() > right.as_cast());
472        self.engine
473            .register_fn(">=", |left: B, right: A| left.as_cast() >= right.as_cast());
474        self.engine
475            .register_fn("<", |left: B, right: A| left.as_cast() < right.as_cast());
476        self.engine
477            .register_fn("<=", |left: B, right: A| left.as_cast() <= right.as_cast());
478        self.engine
479            .register_fn("!=", |left: B, right: A| left.as_cast() != right.as_cast());
480        self.engine
481            .register_fn("==", |left: B, right: A| left.as_cast() == right.as_cast());
482    }
483
484    fn register_options<T: Variant + Clone>(&mut self) {
485        self.engine
486            .register_fn("is_some", crate::internal::script_is_some::<T>)
487            .register_fn("unwrap", crate::internal::script_unwrap::<T>)
488            .register_fn("unwrap_or", crate::internal::script_unwrap_or::<T>);
489    }
490}
491
492impl FormattingEngine {
493    /// Creates a new `FormattingEngine` instance.
494    ///
495    /// # Arguments
496    ///
497    /// * `debug` - A boolean indicating whether to enable debug functions inside Rhai.
498    ///
499    /// # Returns
500    ///
501    /// A new, pre-configured, `FormattingEngine` instance.
502    #[must_use]
503    pub fn new(debug: bool) -> Self {
504        build_engine(debug)
505    }
506
507    /// Registers a custom type with the Rhai engine.
508    ///
509    /// This method also registers the `is_some`, `unwrap`, and `unwrap_or` methods for the custom type,
510    /// as well as the necessary functions for using the type inside a `Vec<T>`.
511    ///
512    /// # Type Parameters
513    ///
514    /// * `T` - The type to register, which must implement `Variant` and `Clone`.
515    ///
516    /// # Returns
517    ///
518    /// A mutable reference to `self` to allow method chaining.
519    pub fn register_type<T: Variant + Clone>(&mut self) -> &mut Self {
520        self.engine.register_type::<T>();
521        self.register_vec::<T>();
522        self.register_options::<T>();
523
524        self
525    }
526
527    /// Makes a custom type comparable.
528    ///
529    /// This method also registers the `any`, `none` and `all` methods for the custom type,
530    /// as well as the necessary functions for using the type inside a `Vec<T>`.
531    ///
532    /// # Type Parameters
533    ///
534    /// * `T` - The type to register, which must implement `Variant` and `Clone`.
535    ///
536    /// # Returns
537    ///
538    /// A mutable reference to `self` to allow method chaining.
539    pub fn make_comparable<T: Variant + PartialEq + Clone>(&mut self) -> &mut Self {
540        self.register_type::<T>();
541        self.register_options::<T>();
542        self.register_vec::<T>();
543        self.register_fn("any", crate::internal::script_any_type::<T>)
544            .register_fn("all", crate::internal::script_all_type::<T>)
545            .register_fn("none", crate::internal::script_none_type::<T>);
546        self
547    }
548
549    /// Builds and registers a custom type with the Rhai engine.
550    ///
551    /// This function initializes the custom type using `build_type` and then registers it
552    /// with the Rhai engine, making it accessible within the DSL scripts.
553    ///
554    /// # Type Parameters
555    ///
556    /// * `T` - The custom type to build and register, which must implement `Variant`, `CustomType`, and `Clone`.
557    ///
558    /// # Returns
559    ///
560    /// A mutable reference to `self` to allow method chaining.
561    ///
562    /// # Example
563    ///
564    /// ```rust
565    /// use script_format::{
566    ///     rhai::{CustomType, TypeBuilder},
567    ///     FormattingEngine,
568    /// };
569    ///
570    /// #[derive(Clone, CustomType)]
571    /// struct Person {
572    ///     name: String,
573    ///     age: i32,
574    /// }
575    ///
576    /// let mut engine = FormattingEngine::new(false);
577    /// engine.build_type::<Person>();
578    /// ```
579    pub fn build_type<T: Variant + CustomType + Clone>(&mut self) -> &mut Self {
580        self.engine.build_type::<T>();
581        self.register_type::<T>();
582        self
583    }
584
585    /// Runs the formatting script with the provided scope.
586    ///
587    /// Use the scope to pass data to the script.
588    /// **You must register (`build_type` / `register_type`) each custom type that should be accessed beforehand so that rhai can properly access it.**
589    ///
590    /// # Arguments
591    ///
592    /// * `scope` - The Rhai `Scope` to be used during script evaluation.
593    /// * `script` - The script to execute for formatting.
594    ///
595    /// # Errors
596    ///
597    /// Returns an error if script execution fails.
598    ///
599    /// # Returns
600    ///
601    /// A formatted string result.
602    pub fn format_with_scope(&mut self, scope: &mut Scope, script: &str) -> ScriptResult<String> {
603        scope.push_constant("NL", "\n");
604
605        self.messages.borrow_mut().clear();
606        self.engine.run_with_scope(scope, script)?;
607
608        Ok(self.messages.borrow().join(""))
609    }
610
611    /// Formats an object using a script from a file with the provided scope.
612    ///
613    /// Use the scope to pass data to the script.
614    /// **You must register (`build_type` / `register_type`) each custom type that should be accessed beforehand so that rhai can properly access it.**
615    ///
616    /// # Arguments
617    ///
618    /// * `scope` - The Rhai `Scope` to be used during script evaluation.
619    /// * `script` - The file path of the script to execute.
620    ///
621    /// # Errors
622    ///
623    /// Returns an error if file reading or script execution fails.
624    ///
625    /// # Returns
626    ///
627    /// A formatted string result.
628    #[cfg(not(feature = "web"))]
629    pub fn format_from_file_with_scope<P: AsRef<Path>>(
630        &mut self,
631        scope: &mut Scope,
632        script: P,
633    ) -> ScriptResult<String> {
634        match std::fs::read_to_string(script.as_ref()) {
635            Ok(script) => self.format_with_scope(scope, &script),
636            Err(e) => Err(e.to_string().into()),
637        }
638    }
639
640    /// Clones the messages buffer.
641    ///
642    /// This can be useful if you want to register a custom function that needs to access the messages buffer.
643    ///
644    /// # Returns
645    ///
646    /// A reference-counted pointer to the messages buffer.
647    pub fn clone_messages(&self) -> Rc<RefCell<Vec<String>>> {
648        self.messages.clone()
649    }
650
651    /// Formats an object using a script.
652    ///
653    /// **You must register (`build_type` / `register_type`) each custom type that should be accessed beforehand so that rhai can properly access it.**
654    ///
655    /// # Arguments
656    ///
657    /// * `name` - The name of the data variable in the script.
658    /// * `data` - The data object to format.
659    /// * `script` - The script to execute for formatting.
660    ///
661    /// # Errors
662    ///
663    /// Returns an error if script execution fails.
664    ///
665    /// # Returns
666    ///
667    /// A formatted string result.
668    pub fn format<T: Variant + Clone>(
669        &mut self,
670        name: &str,
671        data: T,
672        script: &str,
673    ) -> ScriptResult<String> {
674        self.register_type::<T>();
675
676        let mut scope = Scope::new();
677        scope.push_constant(name, data);
678
679        self.format_with_scope(&mut scope, script)
680    }
681
682    /// Formats an object using a script from a file.
683    ///
684    /// **You must register (`build_type` / `register_type`) each custom type that should be accessed beforehand so that rhai can properly access it.**
685    ///
686    /// # Arguments
687    ///
688    /// * `name` - The name of the data variable in the script.
689    /// * `data` - The data object to format.
690    /// * `script` - The file path of the script to execute.
691    ///
692    /// # Errors
693    ///
694    /// Returns an error if file reading or script execution fails.
695    ///
696    /// # Returns
697    ///
698    /// A formatted string result.
699    #[cfg(not(feature = "web"))]
700    pub fn format_from_file<T: Variant + Clone, P: AsRef<Path>>(
701        &mut self,
702        name: &str,
703        data: T,
704        script: P,
705    ) -> ScriptResult<String> {
706        match std::fs::read_to_string(script.as_ref()) {
707            Ok(script) => self.format(name, data, &script),
708            Err(e) => Err(e.to_string().into()),
709        }
710    }
711}
712
713fn option_to_string<T: std::fmt::Display>(value: Option<T>) -> Option<String> {
714    if let Some(v) = value {
715        Some(format!("{v}"))
716    } else {
717        None
718    }
719}
720
721fn dynamic_to_string(v: Dynamic) -> ScriptResult<Option<String>> {
722    let t = v.type_id();
723    if t == TypeId::of::<()>() {
724        Ok(None)
725    } else if v.is_array() {
726        let flattened = flatten_dynamic(v)?;
727        if flattened.len() > 0 {
728            Ok(Some(flattened.join("")))
729        } else {
730            Ok(None)
731        }
732    } else if t == TypeId::of::<Option<&str>>() {
733        Ok(option_to_string::<&str>(v.cast()))
734    } else if t == TypeId::of::<Option<bool>>() {
735        Ok(option_to_string::<bool>(v.cast()))
736    } else if t == TypeId::of::<Option<i8>>() {
737        Ok(option_to_string::<i8>(v.cast()))
738    } else if t == TypeId::of::<Option<i16>>() {
739        Ok(option_to_string::<i16>(v.cast()))
740    } else if t == TypeId::of::<Option<i32>>() {
741        Ok(option_to_string::<i32>(v.cast()))
742    } else if t == TypeId::of::<Option<i64>>() {
743        Ok(option_to_string::<i64>(v.cast()))
744    } else if t == TypeId::of::<Option<i128>>() {
745        Ok(option_to_string::<i128>(v.cast()))
746    } else if t == TypeId::of::<Option<u8>>() {
747        Ok(option_to_string::<u8>(v.cast()))
748    } else if t == TypeId::of::<Option<u16>>() {
749        Ok(option_to_string::<u16>(v.cast()))
750    } else if t == TypeId::of::<Option<u32>>() {
751        Ok(option_to_string::<u32>(v.cast()))
752    } else if t == TypeId::of::<Option<u64>>() {
753        Ok(option_to_string::<u64>(v.cast()))
754    } else if t == TypeId::of::<Option<f32>>() {
755        Ok(option_to_string::<f32>(v.cast()))
756    } else if t == TypeId::of::<Option<f64>>() {
757        Ok(option_to_string::<f64>(v.cast()))
758    } else if t == TypeId::of::<Option<u128>>() {
759        Ok(option_to_string::<u128>(v.cast()))
760    } else if t == TypeId::of::<Option<char>>() {
761        Ok(option_to_string::<char>(v.cast()))
762    } else if t == TypeId::of::<Option<String>>() {
763        Ok(if let Some(v) = v.cast::<Option<String>>() {
764            Some(v)
765        } else {
766            None
767        })
768    } else if t == TypeId::of::<Option<Dynamic>>() {
769        Ok(if let Some(v) = v.cast::<Option<Dynamic>>() {
770            dynamic_to_string(v)?
771        } else {
772            None
773        })
774    } else if t == TypeId::of::<bool>() {
775        Ok(Some(v.as_bool()?.to_string()))
776    } else if t == TypeId::of::<ImmutableString>() {
777        Ok(Some(v.into_immutable_string()?.to_string()))
778    } else if t == TypeId::of::<char>() {
779        Ok(Some(v.as_char()?.to_string()))
780    } else if t == TypeId::of::<INT>() {
781        Ok(Some(v.as_int()?.to_string()))
782    } else if t == TypeId::of::<FLOAT>() {
783        Ok(Some(v.as_float()?.to_string()))
784    } else {
785        eprintln!("{}", v.type_name());
786        Err("unsupported type".into())
787    }
788}
789
790fn flatten_dynamic(vs: Dynamic) -> Result<Vec<String>, Box<EvalAltResult>> {
791    let mut res = Vec::new();
792    if vs.is_array() {
793        for v in vs.into_array()? {
794            if v.is_array() {
795                let mut values = flatten_dynamic(v)?;
796                res.append(&mut values);
797            } else {
798                if let Some(s) = dynamic_to_string(v)? {
799                    res.push(s);
800                }
801            }
802        }
803    } else if vs.type_id() == TypeId::of::<Vec<String>>() {
804        let mut vs = vs.cast::<Vec<String>>();
805        res.append(&mut vs);
806    } else if let Some(s) = dynamic_to_string(vs)? {
807        res.push(s);
808    }
809    Ok(res)
810}
811
812#[allow(clippy::too_many_lines)]
813fn build_engine(debug: bool) -> FormattingEngine {
814    let mut engine = FormattingEngine {
815        engine: rhai::Engine::new(),
816        messages: Rc::new(RefCell::new(Vec::new())),
817    };
818    engine.set_max_expr_depths(128, 64);
819
820    let package = CorePackage::new();
821
822    engine.register_global_module(package.as_shared_module());
823
824    // Register the custom syntax
825    {
826        let messages = engine.clone_messages();
827        engine
828            .register_custom_syntax(
829                ["~", "$expr$"],
830                true,
831                move |context: &mut rhai::EvalContext,
832                      inputs: &[rhai::Expression]|
833                      -> ScriptResult<Dynamic> {
834                    for e in inputs {
835                        let result = context.eval_expression_tree(&e)?;
836                        let mut m = flatten_dynamic(result)?;
837                        messages.borrow_mut().append(&mut m);
838                    }
839                    Ok(Dynamic::UNIT)
840                },
841            )
842            .unwrap();
843    }
844
845    let indent = Rc::new(RefCell::new("    ".to_owned()));
846
847    {
848        let indent = indent.clone();
849
850        // This isn't deprecated, the api is just volatile and may change
851        #[allow(deprecated)]
852        engine.on_var(move |name, _, _| match name {
853            "IND" => Ok(Some(indent.borrow().clone().into())),
854            _ => Ok(None),
855        });
856    }
857
858    {
859        let indent = indent.clone();
860        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
861        engine.register_fn("IND", move |count: i64| {
862            indent.borrow().repeat(count as usize)
863        });
864    }
865
866    {
867        let indent = indent.clone();
868        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
869        engine.register_fn("SET_INDENT", move |value: &str| {
870            value.clone_into(&mut indent.borrow_mut());
871        });
872    }
873
874    {
875        let indent = indent.clone();
876        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
877        engine.register_fn("SET_INDENT", move |value: &str, count: i64| {
878            *indent.borrow_mut() = value.repeat(count as usize)
879        });
880    }
881
882    {
883        let indent = indent.clone();
884        #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
885        engine.register_fn("SET_INDENT", move |count: i64| {
886            *indent.borrow_mut() = " ".repeat(count as usize)
887        });
888    }
889
890    #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
891    engine.register_fn("NL", |count: i64| "\n".repeat(count as usize));
892
893    engine.register_iterator::<Vec<serde_value::Value>>();
894
895    engine.register_options::<&str>();
896    engine.register_options::<String>();
897    engine.register_options::<bool>();
898    engine.register_options::<i64>();
899    engine.register_options::<u64>();
900    engine.register_options::<i32>();
901    engine.register_options::<u32>();
902    engine.register_options::<i16>();
903    engine.register_options::<u16>();
904    engine.register_options::<i8>();
905    engine.register_options::<u8>();
906    engine.register_options::<usize>();
907    engine.register_options::<isize>();
908    engine.register_options::<i128>();
909    engine.register_options::<u128>();
910    engine.register_options::<f32>();
911    engine.register_options::<f64>();
912
913    engine.register_type_dynamic::<i8, i64>(Dynamic::is_int, Dynamic::as_int);
914    engine.register_type_dynamic::<i16, i64>(Dynamic::is_int, Dynamic::as_int);
915    engine.register_type_dynamic::<i32, i64>(Dynamic::is_int, Dynamic::as_int);
916    engine.register_type_dynamic::<i64, i64>(Dynamic::is_int, Dynamic::as_int);
917
918    engine.register_type_dynamic::<u8, i64>(Dynamic::is_int, Dynamic::as_int);
919    engine.register_type_dynamic::<u16, i64>(Dynamic::is_int, Dynamic::as_int);
920    engine.register_type_dynamic::<u32, i64>(Dynamic::is_int, Dynamic::as_int);
921
922    engine.register_type_dynamic::<f32, f64>(Dynamic::is_float, Dynamic::as_float);
923    engine.register_type_dynamic::<f64, f64>(Dynamic::is_float, Dynamic::as_float);
924
925    engine.register_type_dynamic::<bool, bool>(Dynamic::is_bool, Dynamic::as_bool);
926
927    fn dynamic_as_string(dynamic: &Dynamic) -> Result<String, &'static str> {
928        dynamic.to_owned().into_string()
929    }
930
931    engine.register_type_dynamic::<String, String>(Dynamic::is_string, dynamic_as_string);
932    engine.register_type_dynamic::<&str, String>(Dynamic::is_string, dynamic_as_string);
933
934    engine
935        .register_fn("join", crate::internal::script_join)
936        .register_fn("split", crate::internal::script_split)
937        .register_fn("splitn", crate::internal::script_splitn)
938        .register_fn("rsplitn", crate::internal::script_rsplitn)
939        .register_fn("is_empty", crate::internal::script_string_is_empty)
940        .register_fn("is_empty", crate::internal::script_array_is_empty)
941        .register_fn("starts_with", crate::internal::script_starts_with)
942        .register_fn("ends_with", crate::internal::script_ends_with)
943        .register_fn("trim", crate::internal::script_trim)
944        .register_fn("is_string", crate::internal::script_is_no_string)
945        .register_fn("is_string", crate::internal::script_is_string);
946
947    // DSL
948    engine.build_type::<ToBe>();
949    engine
950        .register_custom_operator("zip", 65)
951        .unwrap()
952        .register_custom_operator("to_be", 60)
953        .unwrap()
954        .register_custom_operator("and", 60)
955        .unwrap()
956        .register_fn("and", |a: bool, b: bool| a && b)
957        .register_custom_operator("or", 30)
958        .unwrap()
959        .register_fn("or", |a: bool, b: bool| a || b)
960        .register_custom_operator("xor", 30)
961        .unwrap()
962        .register_fn("xor", |a: bool, b: bool| a ^ b)
963        .register_custom_operator("contains", 25)
964        .unwrap()
965        .register_custom_operator("equals", 25)
966        .unwrap()
967        .register_custom_operator("require", 25)
968        .unwrap()
969        .register_custom_operator("any", 25)
970        .unwrap()
971        .register_custom_operator("all", 25)
972        .unwrap()
973        .register_custom_operator("none", 25)
974        .unwrap()
975        .register_fn("contains", crate::internal::script_map_contains)
976        .register_fn("contains", crate::internal::script_string_contains)
977        .register_fn("equals", crate::internal::script_map_equals)
978        .register_fn("equals", crate::internal::script_value_equals)
979        .register_fn("equals", crate::internal::script_array_equals)
980        .register_fn("contains", crate::internal::script_array_contains)
981        .register_fn("require", crate::internal::script_require)
982        .register_fn("to_be", crate::internal::script_to_be)
983        .register_fn("any", crate::internal::script_any)
984        .register_fn("any", crate::internal::script_any_void)
985        .register_fn("all", crate::internal::script_all)
986        .register_fn("all", crate::internal::script_all_void)
987        .register_fn("none", crate::internal::script_none)
988        .register_fn("none", crate::internal::script_none_void);
989
990    engine.register_msg_single::<&str>();
991    engine.register_msg_single::<String>();
992    engine.register_msg_single::<bool>();
993    engine.register_msg_single::<i64>();
994    engine.register_msg_single::<u64>();
995    engine.register_msg_single::<i32>();
996    engine.register_msg_single::<u32>();
997    engine.register_msg_single::<i16>();
998    engine.register_msg_single::<u16>();
999    engine.register_msg_single::<i8>();
1000    engine.register_msg_single::<u8>();
1001    engine.register_msg_single::<usize>();
1002    engine.register_msg_single::<isize>();
1003    engine.register_msg_single::<i128>();
1004    engine.register_msg_single::<u128>();
1005    engine.register_msg_single::<f32>();
1006    engine.register_msg_single::<f64>();
1007
1008    engine.register_msg::<&str>();
1009    engine.register_msg::<String>();
1010    engine.register_msg::<bool>();
1011    engine.register_msg::<i64>();
1012    engine.register_msg::<u64>();
1013    engine.register_msg::<i32>();
1014    engine.register_msg::<u32>();
1015    engine.register_msg::<i16>();
1016    engine.register_msg::<u16>();
1017    engine.register_msg::<i8>();
1018    engine.register_msg::<u8>();
1019    engine.register_msg::<usize>();
1020    engine.register_msg::<isize>();
1021    engine.register_msg::<i128>();
1022    engine.register_msg::<u128>();
1023    engine.register_msg::<f32>();
1024    engine.register_msg::<f64>();
1025
1026    engine.register_comparison::<u8, u8, u8>();
1027    engine.register_comparison::<u8, u16, u16>();
1028    engine.register_comparison::<u8, u32, u32>();
1029    engine.register_comparison::<u8, u64, u64>();
1030    engine.register_comparison::<u8, usize, u128>();
1031    engine.register_comparison::<u8, u128, u128>();
1032
1033    engine.register_comparison::<u16, u16, u16>();
1034    engine.register_comparison::<u16, u32, u32>();
1035    engine.register_comparison::<u16, u64, u64>();
1036    engine.register_comparison::<u16, usize, u128>();
1037    engine.register_comparison::<u16, u128, u128>();
1038
1039    engine.register_comparison::<u32, u32, u32>();
1040    engine.register_comparison::<u32, u64, u64>();
1041    engine.register_comparison::<u32, usize, u128>();
1042    engine.register_comparison::<u32, u128, u128>();
1043
1044    engine.register_comparison::<u64, u64, u64>();
1045    engine.register_comparison::<u64, usize, u128>();
1046    engine.register_comparison::<u64, u128, u128>();
1047
1048    engine.register_comparison::<usize, usize, u128>();
1049    engine.register_comparison::<usize, u128, u128>();
1050
1051    engine.register_comparison::<u128, u128, u128>();
1052
1053    engine.register_comparison::<i8, i8, i8>();
1054    engine.register_comparison::<i8, i16, i16>();
1055    engine.register_comparison::<i8, i32, i32>();
1056    engine.register_comparison::<i8, i64, i64>();
1057    engine.register_comparison::<i8, isize, i128>();
1058    engine.register_comparison::<i8, i128, i128>();
1059
1060    engine.register_comparison::<i16, i16, i16>();
1061    engine.register_comparison::<i16, i32, i32>();
1062    engine.register_comparison::<i16, i64, i64>();
1063    engine.register_comparison::<i8, isize, i128>();
1064    engine.register_comparison::<i16, i128, i128>();
1065
1066    engine.register_comparison::<i32, i32, i32>();
1067    engine.register_comparison::<i32, i64, i64>();
1068    engine.register_comparison::<i32, isize, i128>();
1069    engine.register_comparison::<i32, i128, i128>();
1070
1071    engine.register_comparison::<i64, i64, i64>();
1072    engine.register_comparison::<i64, isize, i128>();
1073    engine.register_comparison::<i64, i128, i128>();
1074
1075    engine.register_comparison::<isize, isize, i128>();
1076    engine.register_comparison::<isize, i128, i128>();
1077
1078    engine.register_comparison::<i128, i128, i128>();
1079
1080    engine.register_comparison::<f32, f32, f32>();
1081    engine.register_comparison::<f32, f64, f64>();
1082
1083    engine.register_comparison::<u8, f32, f32>();
1084    engine.register_comparison::<u16, f32, f32>();
1085    engine.register_comparison::<u32, f64, f64>();
1086
1087    engine.register_comparison::<i8, f32, f32>();
1088    engine.register_comparison::<i16, f32, f32>();
1089    engine.register_comparison::<i32, f64, f64>();
1090
1091    engine.register_value::<&str>();
1092    engine.register_value::<String>();
1093    engine.register_value::<bool>();
1094    engine.register_value::<i64>();
1095    engine.register_value::<u64>();
1096    engine.register_value::<i32>();
1097    engine.register_value::<u32>();
1098    engine.register_value::<i16>();
1099    engine.register_value::<u16>();
1100    engine.register_value::<i8>();
1101    engine.register_value::<u8>();
1102    engine.register_value::<usize>();
1103    engine.register_value::<isize>();
1104    engine.register_value::<i128>();
1105    engine.register_value::<u128>();
1106    engine.register_value::<f32>();
1107    engine.register_value::<f64>();
1108
1109    {
1110        let messages = engine.clone_messages();
1111        engine.register_fn("-", move |msg: Dynamic| -> ScriptResult<()> {
1112            minus_deprecation();
1113            if msg.is_array() {
1114                let arr = msg.into_array().unwrap();
1115                for m in arr {
1116                    if let Some(msg) = dynamic_to_string(m)? {
1117                        messages.borrow_mut().push(msg);
1118                    }
1119                }
1120            } else {
1121                if let Some(msg) = dynamic_to_string(msg)? {
1122                    messages.borrow_mut().push(msg);
1123                }
1124            }
1125            Ok(())
1126        });
1127    }
1128
1129    {
1130        let messages = engine.clone_messages();
1131        engine.register_fn("-", move |msg: serde_value::Value| {
1132            minus_deprecation();
1133            messages
1134                .borrow_mut()
1135                .push(serde_json::to_string(&msg).unwrap());
1136        });
1137    }
1138
1139    engine.register_fn("++", move |a: serde_value::Value, b: serde_value::Value| {
1140        vec![
1141            serde_json::to_string(&a).unwrap(),
1142            serde_json::to_string(&b).unwrap(),
1143        ]
1144    });
1145
1146    engine.register_concat::<&str>();
1147    engine.register_concat::<String>();
1148    engine.register_concat::<bool>();
1149    engine.register_concat::<i64>();
1150    engine.register_concat::<u64>();
1151    engine.register_concat::<i32>();
1152    engine.register_concat::<u32>();
1153    engine.register_concat::<i16>();
1154    engine.register_concat::<u16>();
1155    engine.register_concat::<i8>();
1156    engine.register_concat::<u8>();
1157    engine.register_concat::<usize>();
1158    engine.register_concat::<isize>();
1159    engine.register_concat::<i128>();
1160    engine.register_concat::<u128>();
1161    engine.register_concat::<f32>();
1162    engine.register_concat::<f64>();
1163
1164    engine.register_vec_printable::<&str>();
1165    engine.register_vec_printable::<String>();
1166    engine.register_vec_printable::<bool>();
1167    engine.register_vec_printable::<i64>();
1168    engine.register_vec_printable::<u64>();
1169    engine.register_vec_printable::<i32>();
1170    engine.register_vec_printable::<u32>();
1171    engine.register_vec_printable::<i16>();
1172    engine.register_vec_printable::<u16>();
1173    engine.register_vec_printable::<i8>();
1174    engine.register_vec_printable::<u8>();
1175    engine.register_vec_printable::<usize>();
1176    engine.register_vec_printable::<isize>();
1177    engine.register_vec_printable::<i128>();
1178    engine.register_vec_printable::<u128>();
1179    engine.register_vec_printable::<f32>();
1180    engine.register_vec_printable::<f64>();
1181
1182    engine.register_vec::<()>();
1183
1184    engine
1185        .register_fn("any", crate::internal::script_any_type::<bool>)
1186        .register_fn("all", crate::internal::script_any_type::<bool>)
1187        .register_fn("none", crate::internal::script_any_type::<bool>)
1188        .register_fn("++", move |(): (), b: &str| vec![b.to_owned()])
1189        .register_fn("++", move |(): (), b: usize| vec![b.to_string()])
1190        .register_custom_operator("++", 15)
1191        .unwrap()
1192        .register_custom_operator("then_emit", 20)
1193        .unwrap()
1194        .register_fn(
1195            "then_emit",
1196            move |a: bool, msg: &str| {
1197                if a {
1198                    Some(msg.to_owned())
1199                } else {
1200                    None
1201                }
1202            },
1203        )
1204        .register_custom_operator("or_emit", 20)
1205        .unwrap()
1206        .register_fn(
1207            "or_emit",
1208            move |a: bool, msg: &str| {
1209                if !a {
1210                    Some(msg.to_owned())
1211                } else {
1212                    None
1213                }
1214            },
1215        );
1216    // END DSL
1217
1218    if debug {
1219        engine.on_print(move |x| eprintln!("INFO => {x}"));
1220        engine.on_debug(move |x, _, pos| eprintln!("DEBUG({pos:?}) => {x}"));
1221    } else {
1222        engine.on_print(|_| ());
1223        engine.on_debug(|_, _, _| ());
1224    }
1225
1226    engine.disable_symbol("eval");
1227
1228    engine
1229}
1230
1231macro_rules! impl_as_cast {
1232    ($A: ty, ($($B: ty),*)) => {
1233        $(
1234            impl AsCast<$B> for $A {
1235                fn as_cast(self) -> $B {
1236                    self as $B
1237                }
1238            }
1239        )*
1240    }
1241}
1242
1243trait AsCast<T> {
1244    fn as_cast(self) -> T;
1245}
1246
1247impl_as_cast!(u8, (u8, u16, u32, u64, u128, usize));
1248impl_as_cast!(i8, (i8, i16, i32, i64, i128, isize));
1249
1250impl_as_cast!(u16, (u16, u32, u64, u128, usize));
1251impl_as_cast!(i16, (i16, i32, i64, i128, isize));
1252
1253impl_as_cast!(u32, (u32, u64, u128, usize));
1254impl_as_cast!(i32, (i32, i64, i128, isize));
1255
1256impl_as_cast!(u64, (u64, u128));
1257impl_as_cast!(i64, (i64, i128));
1258
1259impl_as_cast!(usize, (usize, u128));
1260impl_as_cast!(isize, (isize, i128));
1261
1262impl_as_cast!(u128, (u128));
1263impl_as_cast!(i128, (i128));
1264
1265impl_as_cast!(f32, (f32, f64));
1266impl_as_cast!(f64, (f64));
1267
1268impl_as_cast!(u8, (f32, f64));
1269impl_as_cast!(u16, (f32, f64));
1270impl_as_cast!(u32, (f64));
1271
1272impl_as_cast!(i8, (f32, f64));
1273impl_as_cast!(i16, (f32, f64));
1274impl_as_cast!(i32, (f64));