Skip to main content

ad_astra/runtime/
coercion.rs

1////////////////////////////////////////////////////////////////////////////////
2// This file is part of "Ad Astra", an embeddable scripting programming       //
3// language platform.                                                         //
4//                                                                            //
5// This work is proprietary software with source-available code.              //
6//                                                                            //
7// To copy, use, distribute, or contribute to this work, you must agree to    //
8// the terms of the General License Agreement:                                //
9//                                                                            //
10// https://github.com/Eliah-Lakhin/ad-astra/blob/master/EULA.md               //
11//                                                                            //
12// The agreement grants a Basic Commercial License, allowing you to use       //
13// this work in non-commercial and limited commercial products with a total   //
14// gross revenue cap. To remove this commercial limit for one of your         //
15// products, you must acquire a Full Commercial License.                      //
16//                                                                            //
17// If you contribute to the source code, documentation, or related materials, //
18// you must grant me an exclusive license to these contributions.             //
19// Contributions are governed by the "Contributions" section of the General   //
20// License Agreement.                                                         //
21//                                                                            //
22// Copying the work in parts is strictly forbidden, except as permitted       //
23// under the General License Agreement.                                       //
24//                                                                            //
25// If you do not or cannot agree to the terms of this Agreement,              //
26// do not use this work.                                                      //
27//                                                                            //
28// This work is provided "as is", without any warranties, express or implied, //
29// except where such disclaimers are legally invalid.                         //
30//                                                                            //
31// Copyright (c) 2024 Ilya Lakhin (Илья Александрович Лахин).                 //
32// All rights reserved.                                                       //
33////////////////////////////////////////////////////////////////////////////////
34
35use std::{
36    any::TypeId,
37    mem::{take, transmute},
38    sync::Arc,
39};
40
41use crate::runtime::{
42    memory::MemorySlice,
43    Cell,
44    Origin,
45    RuntimeError,
46    RuntimeResult,
47    ScriptType,
48    TypeHint,
49    TypeMeta,
50};
51
52/// A trait that casts Script data into Rust data.
53///
54/// By implementing the Downcast trait on a Rust type, you make this type
55/// eligible to be part of the parameter signature of exported functions.
56///
57/// The Script Engine uses the Downcast implementation of the Rust type to
58/// transform a call containing Script data into a Rust object, passing this
59/// object into the exported Rust function as a function argument.
60///
61/// ```
62/// # use ad_astra::export;
63/// #
64/// # #[export(include)]
65/// # #[export(package)]
66/// # #[derive(Default)]
67/// # struct Package;
68/// #
69/// #[export]
70/// fn foo(_x: usize) {} // The usize type implements the Downcast trait.
71/// ```
72///
73/// The opposite operation of transforming Rust data into Script data is
74/// provided through the separate [Upcast] trait.
75///
76/// ## Automatic Implementation
77///
78/// When you use the [export](crate::export) macro to export a Rust structure
79/// `Foo`, the macro automatically implements the Downcast trait for `Foo`,
80/// `&Foo`, and `&mut Foo`. This makes these types usable as parameters in
81/// exported functions.
82///
83/// ```
84/// # use ad_astra::{
85/// #     export,
86/// #     runtime::{Cell, Downcast, Origin, Provider},
87/// # };
88/// #
89/// # #[export(include)]
90/// # #[export(package)]
91/// # #[derive(Default)]
92/// # struct Package;
93/// #
94/// #[export]
95/// #[derive(Clone)]
96/// pub struct Foo;
97///
98/// let foo = Cell::give(Origin::nil(), Foo).unwrap();
99///
100/// // Foo cannot be downcasted to usize.
101/// assert!(<usize>::downcast(Origin::nil(), Provider::Owned(foo.clone())).is_err());
102///
103/// // Foo can be downcasted to Foo.
104/// assert!(<Foo>::downcast(Origin::nil(), Provider::Owned(foo.clone())).is_ok());
105///
106/// // Foo can be downcasted to &Foo.
107/// let mut foo = foo.clone();
108/// assert!(<&Foo>::downcast(Origin::nil(), Provider::Borrowed(&mut foo)).is_ok());
109///
110/// // Foo can be downcasted to &mut Foo.
111/// let mut foo = foo.clone();
112/// assert!(<&mut Foo>::downcast(Origin::nil(), Provider::Borrowed(&mut foo)).is_ok());
113///
114/// // The Foo, &Foo, and &mut Foo types are eligible as types in the exported
115/// // function signature.
116/// #[export]
117/// fn exported_function(foo1: Foo, foo2: &Foo, foo3: &mut Foo) {}
118/// ```
119///
120/// ## Manual Implementation
121///
122/// To manually implement the Downcast trait for an exported Rust structure,
123/// you should export a type alias to this structure instead of the structure
124/// itself. The macro system does not automatically implement Downcast for
125/// type aliases, allowing for manual implementation.
126///
127/// Generally, the Downcast trait can be manually implemented on any Rust type,
128/// not just the exported type.
129///
130/// This allows for providing custom Rust representations of Script data. For
131/// example, this Ad Astra crate implements Downcast for the [Option] container,
132/// even though Option is not a [ScriptType].
133///
134/// When you downcast a Cell to an Option, the underlying implementation wraps
135/// the Cell's data into [Some] if the Cell is not [nil](Cell::nil); otherwise,
136/// the downcast procedure returns [None].
137///
138/// ```
139/// # use ad_astra::{
140/// #     export,
141/// #     runtime::{Cell, Downcast, Origin, Provider},
142/// # };
143/// #
144/// # #[export(include)]
145/// # #[export(package)]
146/// # #[derive(Default)]
147/// # struct Package;
148/// #
149/// assert_eq!(
150///     <Option<usize>>::downcast(Origin::nil(), Provider::Owned(Cell::nil())).unwrap(),
151///     None,
152/// );
153///
154/// assert_eq!(
155///     <Option<usize>>::downcast(
156///         Origin::nil(),
157///         Provider::Owned(Cell::give(Origin::nil(), 100usize).unwrap())
158///     )
159///     .unwrap(),
160///     Some(100usize),
161/// );
162///
163/// assert_eq!(
164///     <Option<Option<f64>>>::downcast(
165///         Origin::nil(),
166///         Provider::Owned(Cell::give(Origin::nil(), 10.5f64).unwrap())
167///     )
168///     .unwrap(),
169///     Some(Some(10.5f64)),
170/// );
171///
172/// #[export]
173/// fn exported_function(_x: Option<usize>, _u: Option<Option<f64>>) {}
174/// ```
175///
176/// ## Type Casting
177///
178/// Using the Downcast trait, you can perform non-trivial casting of independent
179/// data types. For example, through the Downcast trait (and the [Upcast] trait),
180/// the Ad Astra crate provides type casting between standard built-in primitive
181/// numeric types.
182///
183/// ```
184/// # use ad_astra::runtime::{Cell, Downcast, Origin, Provider};
185/// #
186/// let num = Cell::give(Origin::nil(), 100u32).unwrap();
187///
188/// assert_eq!(
189///     <u32>::downcast(Origin::nil(), Provider::Owned(num.clone())).unwrap(),
190///     100u32,
191/// );
192///
193/// assert_eq!(
194///     <f64>::downcast(Origin::nil(), Provider::Owned(num.clone())).unwrap(),
195///     100.0f64,
196/// );
197/// ```
198///
199/// To implement type casting manually, you can use the [Cell::type_match]
200/// function, which returns a helper [TypeMatch] object. This object allows you
201/// to enumerate the possible Script types of the Cell and handle each case
202/// manually, providing the corresponding type castings. If the Cell's type does
203/// not match any of the expected types, you should call [TypeMatch::mismatch]
204/// at the end of the implementation. This will return a descriptive
205/// [RuntimeError] containing each type that you attempted to handle.
206///
207/// ```
208/// # use ad_astra::{
209/// #     export,
210/// #     runtime::{Cell, Downcast, Origin, Provider, RuntimeResult, ScriptType, TypeHint},
211/// # };
212/// #
213/// #[derive(Debug, PartialEq)]
214/// struct Foo(bool);
215///
216/// // Exporting the type alias instead of the struct turns off automatic
217/// // implementations of the Downcast trait on this struct. Therefore, you can
218/// // implement the trait manually.
219/// #[export]
220/// type FooAlias = Foo;
221///
222/// impl<'a> Downcast<'a> for Foo {
223///     fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
224///         let cell = provider.to_owned();
225///
226///         let mut type_match = cell.type_match();
227///
228///         // If the provided Cell is "Foo", no casting is needed; we can take
229///         // its data as it is.
230///         if type_match.is::<Foo>() {
231///             return cell.take::<Foo>(origin);
232///         }
233///
234///         // If the provided Cell is "bool", the downcast function wraps this
235///         // value into Foo.
236///         if type_match.is::<bool>() {
237///             let inner = cell.take::<bool>(origin)?;
238///
239///             return Ok(Foo(inner));
240///         }
241///
242///         // Otherwise, return an error. The Foo object cannot be
243///         // constructed from any Script type that this downcasting procedure
244///         // supports.
245///         Err(type_match.mismatch(origin))
246///     }
247///
248///     fn hint() -> TypeHint {
249///         Foo::type_meta().into()
250///     }
251/// }
252///
253/// assert_eq!(
254///     Foo::downcast(
255///         Origin::nil(),
256///         Provider::Owned(Cell::give_vec(Origin::nil(), vec![Foo(true)]).unwrap())
257///     )
258///     .unwrap(),
259///     Foo(true),
260/// );
261///
262/// assert_eq!(
263///     Foo::downcast(
264///         Origin::nil(),
265///         Provider::Owned(Cell::give(Origin::nil(), true).unwrap())
266///     )
267///     .unwrap(),
268///     Foo(true),
269/// );
270///
271/// assert!(Foo::downcast(
272///     Origin::nil(),
273///     Provider::Owned(Cell::give(Origin::nil(), 12345usize).unwrap())
274/// )
275/// .is_err());
276/// ```
277///
278/// ## Compositions
279///
280/// When implementing the Downcast trait for a generic container like `Wrapper`,
281/// you can require the generic parameter to also implement Downcast. This
282/// allows you to downcast the generic parameter within the container's Downcast
283/// implementation.
284///
285/// ```
286/// # use ad_astra::runtime::{Cell, Downcast, Origin, Provider, RuntimeResult, TypeHint};
287/// #
288/// #[derive(Debug, PartialEq)]
289/// struct Wrapper<T>(T);
290///
291/// impl<'a, T> Downcast<'a> for Wrapper<T>
292/// where
293///     T: Downcast<'a>,
294/// {
295///     fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
296///         let inner = <T as Downcast<'a>>::downcast(origin, provider)?;
297///
298///         Ok(Wrapper(inner))
299///     }
300///
301///     fn hint() -> TypeHint {
302///         <T as Downcast<'a>>::hint()
303///     }
304/// }
305///
306/// assert_eq!(
307///     <Wrapper<Wrapper<usize>>>::downcast(
308///         Origin::nil(),
309///         Provider::Owned(Cell::give(Origin::nil(), 100usize).unwrap()),
310///     )
311///     .unwrap(),
312///     Wrapper(Wrapper(100usize))
313/// );
314/// ```
315///
316/// In this setup, compositions of Downcast types become Downcast as well:
317/// `Wrapper<T>`, `Wrapper<Option<T>>`, `Option<Wrapper<T>>`, and other possible
318/// combinations are all Downcast types.
319///
320/// ## Lifetime
321///
322/// The Downcast trait has a lifetime parameter `'a`. This parameter indicates
323/// the lifetime of the target type and the lifetime of the input Cell data.
324///
325/// If the target type is just an owned type with the `'static` lifetime, this
326/// generic parameter does not matter for the implementation. The implementation
327/// is likely to fetch this owned data from the Cell using the [Cell::take]
328/// and related functions.
329///
330/// ```
331/// use ad_astra::{
332///     export,
333///     runtime::{Cell, Downcast, Origin, Provider, RuntimeResult, ScriptType, TypeHint},
334/// };
335///
336/// #[derive(Debug)]
337/// struct Foo;
338///
339/// // Exporting the type alias instead of the struct turns off automatic
340/// // implementations of the Downcast trait on this struct. Therefore, you can
341/// // implement the trait manually.
342/// #[export]
343/// type FooAlias = Foo;
344///
345/// impl<'a> Downcast<'a> for Foo {
346///     fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
347///         // Note that `to_owned` is infallible. Any provided Cell can be
348///         // turned into an owned Cell instance.
349///         let cell = provider.to_owned();
350///
351///         cell.take(origin)
352///     }
353///
354///     fn hint() -> TypeHint {
355///         Foo::type_meta().into()
356///     }
357/// }
358///
359/// // This implementation works perfectly fine with both `Provider::Owned` and
360/// // `Provider::Borrowed`.
361///
362/// assert!(Foo::downcast(
363///     Origin::nil(),
364///     Provider::Owned(Cell::give_vec(Origin::nil(), vec![Foo]).unwrap()),
365/// )
366/// .is_ok());
367///
368/// let mut cell = Cell::give_vec(Origin::nil(), vec![Foo]).unwrap();
369///
370/// assert!(Foo::downcast(Origin::nil(), Provider::Borrowed(&mut cell),).is_ok());
371/// ```
372///
373/// However, if the Downcast trait is implemented for `&'a T` and similar
374/// referential types or their wrappers, the trait implementation will need
375/// to borrow the provided Cell eventually (using the [Cell::borrow_ref],
376/// [Cell::borrow_mut], and similar functions).
377///
378/// The lifetime of the reference received from the Cell's borrowing functions
379/// must match the `'a` lifetime parameter of the Downcast trait. For this
380/// reason, the [Downcast::downcast] function receives a [Provider] type instead
381/// of just a Cell. Provider is a simple wrapper for a Cell that can either be
382/// an owned wrapper (`Provider::Owned(cell)`) or a wrapper for a mutable
383/// reference to the Cell (`Provider::Borrowed(&'a mut cell)`).
384///
385/// Each of these variants can be turned into an owned Cell using
386/// [Provider::to_owned], but only the referential variant is eligible for
387/// borrowing. The [Provider::to_borrowed] function returns a [RuntimeError]
388/// if the variant is not `Borrowed`.
389///
390/// When implementing a Downcast trait for a referential type (e.g., `&'a T` or
391/// `&'a mut T`), you should typically attempt to turn the provider into a
392/// reference to the Cell using the [Provider::to_borrowed] function and then
393/// borrow the underlying data of the Cell.
394///
395/// ```
396/// use ad_astra::{
397///     export,
398///     runtime::{Cell, Downcast, Origin, Provider, RuntimeResult, ScriptType, TypeHint},
399/// };
400///
401/// #[derive(Debug)]
402/// struct Foo;
403///
404/// #[export]
405/// type FooAlias = Foo;
406///
407/// impl<'a> Downcast<'a> for &'a Foo {
408///     fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
409///         // Note that `to_borrowed` can fail if the provider variant is not
410///         // `Borrowed`.
411///         let cell = provider.to_borrowed(&origin)?;
412///
413///         cell.borrow_ref(origin)
414///     }
415///
416///     fn hint() -> TypeHint {
417///         Foo::type_meta().into()
418///     }
419/// }
420///
421/// let mut cell = Cell::give_vec(Origin::nil(), vec![Foo]).unwrap();
422///
423/// // You can downcast `Provider::Borrowed` into `&Foo`.
424/// assert!(<&Foo>::downcast(Origin::nil(), Provider::Borrowed(&mut cell)).is_ok());
425///
426/// let cell = Cell::give_vec(Origin::nil(), vec![Foo]).unwrap();
427///
428/// // But you cannot downcast `Provider::Owned` into `&Foo`.
429/// assert!(<&Foo>::downcast(Origin::nil(), Provider::Owned(cell)).is_err());
430/// ```
431pub trait Downcast<'a>: Sized + Send + Sync + 'a {
432    /// Transforms Script data into Rust data.
433    ///
434    /// The `origin` parameter specifies the range in the Rust or Script source
435    /// code where the transformation has been requested.
436    ///
437    /// The `provider` parameter is a wrapper around a [Cell] that points to the
438    /// Script data that needs to be transformed.
439    ///
440    /// The function returns a [RuntimeError] if the underlying implementation
441    /// is unable to perform the transformation into the requested Rust type.
442    fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self>;
443
444    /// Returns a rough estimation of the Script type (or a set of types) from
445    /// which the target Rust type could be inferred.
446    ///
447    /// The underlying implementation makes a best effort to provide as precise
448    /// type information as possible to support static semantic analysis of the
449    /// script code. However, the resulting [TypeHint] object may be imprecise,
450    /// up to indicating [TypeHint::dynamic], which means that the source
451    /// type(s) are not known at compile time.
452    fn hint() -> TypeHint;
453}
454
455/// A wrapper around a [Cell] that provides either borrowing or owning access
456/// to the Cell's data.
457///
458/// This object is used as a parameter for the [Downcast::downcast] function.
459///
460/// If the Provider owns a Cell (via the `Owned` variant) or borrows it (via the
461/// `Borrowed` variant), the downcast function can take ownership of the Cell's
462/// data. However, the downcast function can only dereference the data of the
463/// Cell if the Provider is borrowing this Cell.
464///
465/// For more details, see the [Downcast's Lifetime](Downcast#lifetime)
466/// documentation.
467pub enum Provider<'a> {
468    /// The Provider only provides ownership access to the Cell's data.
469    Owned(Cell),
470
471    /// The Provider provides both ownership and dereferencing access to the
472    /// Cell.
473    Borrowed(&'a mut Cell),
474}
475
476impl<'a> AsRef<Cell> for Provider<'a> {
477    #[inline(always)]
478    fn as_ref(&self) -> &Cell {
479        match self {
480            Self::Owned(cell) => cell,
481            Self::Borrowed(cell) => cell,
482        }
483    }
484}
485
486impl<'a> Provider<'a> {
487    /// Provides a convenient helper interface for handling Cell data based on
488    /// the [Cell type](Cell::ty).
489    ///
490    /// This function is similar to [Cell::type_match].
491    ///
492    /// For more details, see [TypeMatch].
493    #[inline(always)]
494    pub fn type_match(&'a self) -> TypeMatch<'a> {
495        match self {
496            Self::Owned(cell) => TypeMatch {
497                cell,
498                expected: Vec::new(),
499            },
500
501            Self::Borrowed(cell) => TypeMatch {
502                cell,
503                expected: Vec::new(),
504            },
505        }
506    }
507
508    /// Takes ownership of the Provider's Cell.
509    ///
510    /// This function is infallible regardless of the Provider's variant. If the
511    /// variant is `Borrowed`, this function replaces the referenced instance
512    /// with [Cell::nil] and returns the original Cell instance.
513    #[inline(always)]
514    pub fn to_owned(self) -> Cell {
515        match self {
516            Self::Owned(cell) => cell,
517            Self::Borrowed(cell) => take(cell),
518        }
519    }
520
521    /// Takes a mutable reference to the Provider's Cell.
522    ///
523    /// If the Provider's variant is `Borrowed`, the function returns the
524    /// underlying mutable reference; otherwise, it returns a [RuntimeError].
525    ///
526    /// The `origin` parameter specifies the source code range in Rust or Script
527    /// where the data of the Cell is about to be accessed. This parameter is
528    /// used to create an error if the Provider's variant is `Owned`.
529    #[inline(always)]
530    pub fn to_borrowed(self, origin: &Origin) -> RuntimeResult<&'a mut Cell> {
531        match self {
532            Provider::Owned(_) => Err(RuntimeError::DowncastStatic {
533                access_origin: *origin,
534            }),
535            Provider::Borrowed(cell) => Ok(cell),
536        }
537    }
538}
539
540/// A helper object that provides a convenient way to match on the Cell's type.
541///
542/// This object is created by the [Cell::type_match] or [Provider::type_match]
543/// functions and helps you implement different type casting logic based on the
544/// Cell's data type. It is intended for use in manual implementations of the
545/// [Downcast], [Upcast], [ops](crate::runtime::ops) traits, and other scenarios
546/// where you need to handle Cell data based on the [Cell type](Cell::ty).
547///
548/// The [TypeMatch::is] and [TypeMatch::belongs_to] matching functions return
549/// true if the Cell's data corresponds to a particular Script type. Through
550/// these functions, you enumerate all possible Cell data types that your
551/// implementation supports. Whenever you encounter a supported type (the
552/// matching function returns true), you handle the Cell accordingly and
553/// return a meaningful successful result.
554///
555/// If no matching cases are found, you fall back to the [RuntimeError] that
556/// TypeMatch generates for you by calling the [TypeMatch::mismatch] function.
557/// This function returns a descriptive error indicating that the provided
558/// Cell's type does not match any of the expected types enumerated by the
559/// matching functions.
560///
561/// ```
562/// # use ad_astra::{
563/// #     export,
564/// #     runtime::{Downcast, Origin, Provider, RuntimeResult, ScriptType, TypeHint},
565/// # };
566/// #
567/// # #[derive(Debug, PartialEq)]
568/// # struct Foo(bool);
569/// #
570/// # #[export]
571/// # type FooAlias = Foo;
572/// #
573/// impl<'a> Downcast<'a> for Foo {
574///     fn downcast(origin: Origin, provider: Provider<'a>) -> RuntimeResult<Self> {
575///         let cell = provider.to_owned();
576///
577///         let mut type_match = cell.type_match();
578///
579///         // The Cell type is "Foo". Handling this case.
580///         if type_match.is::<Foo>() {
581///             return cell.take::<Foo>(origin);
582///         }
583///
584///         // The Cell type is "bool". Handling this case.
585///         if type_match.is::<bool>() {
586///             let inner = cell.take::<bool>(origin)?;
587///
588///             return Ok(Foo(inner));
589///         }
590///
591///         // Otherwise, returning an error indicating that the Cell type
592///         // should be either "Foo" or "bool".
593///         Err(type_match.mismatch(origin))
594///     }
595///
596///     fn hint() -> TypeHint {
597///         Foo::type_meta().into()
598///     }
599/// }
600/// ```
601pub struct TypeMatch<'a> {
602    cell: &'a Cell,
603    expected: Vec<&'static TypeMeta>,
604}
605
606impl<'a> TypeMatch<'a> {
607    /// Checks if the [Cell's type](Cell::is) is exactly of type `T`.
608    ///
609    /// If the Cell's type is not `T`, it returns false and remembers that `T`
610    /// is one of the expected types.
611    #[inline(always)]
612    pub fn is<T: ScriptType + ?Sized>(&mut self) -> bool {
613        if self.cell.is::<T>() {
614            return true;
615        }
616
617        let ty = T::type_meta();
618
619        let _ = self.expected.push(ty);
620
621        false
622    }
623
624    /// Checks if the [Cell's type](Cell::ty) and the type `T` belong to the
625    /// same [type family](crate::runtime::TypeFamily).
626    ///
627    /// If the Cell's type does not belong to `T`'s family, it returns false
628    /// and remembers that all types from the `T` type family are the expected
629    /// types.
630    #[inline(always)]
631    pub fn belongs_to<T: ScriptType + ?Sized>(&mut self) -> bool {
632        let family = T::type_meta().family();
633
634        if self.cell.ty().family() == family {
635            return true;
636        }
637
638        for ty in family {
639            let _ = self.expected.push(ty);
640        }
641
642        false
643    }
644
645    /// Returns a reference to the Cell that the TypeMatch object is
646    /// matching on.
647    #[inline(always)]
648    pub fn cell(&self) -> &'a Cell {
649        self.cell
650    }
651
652    /// Creates a [RuntimeError] indicating that the Cell's type does not match
653    /// any of the expected types.
654    ///
655    /// This function should be called as the last statement after all matching
656    /// cases ([is](Self::is) and [belongs_to](Self::belongs_to) functions) have
657    /// been checked, and all of them return false.
658    ///
659    /// The `origin` parameter specifies the Rust or Script source code range
660    /// where the Cell was supposed to be accessed.
661    #[inline(always)]
662    pub fn mismatch(self, origin: Origin) -> RuntimeError {
663        return RuntimeError::TypeMismatch {
664            access_origin: origin,
665            data_type: self.cell.ty(),
666            expected_types: self.expected,
667        };
668    }
669}
670
671impl Cell {
672    /// Provides a convenient helper interface for handling Cell data based on
673    /// the [Cell type](Cell::ty).
674    ///
675    /// For more details, see [TypeMatch].
676    #[inline(always)]
677    pub fn type_match(&self) -> TypeMatch {
678        TypeMatch {
679            cell: self,
680            expected: Vec::new(),
681        }
682    }
683}
684
685/// A trait that casts Rust data into Script data.
686///
687/// By implementing the Upcast trait for a Rust type, you make this type
688/// eligible for returning values from exported functions.
689///
690/// The Script Engine uses the Upcast implementation of a Rust type to
691/// transfer data returned by a Rust function back to the Script Engine.
692///
693/// ```
694/// # use ad_astra::export;
695/// #
696/// # #[export(include)]
697/// # #[export(package)]
698/// # #[derive(Default)]
699/// # struct Package;
700/// #
701/// #[export]
702/// fn foo() -> usize { 123 } // The usize type implements the Upcast trait.
703/// ```
704///
705/// The opposite operation of transferring Script data into Rust is
706/// provided by the separate [Downcast] trait.
707///
708/// ## Automatic Implementation
709///
710/// When you use the [export](crate::export) attribute on a Rust structure
711/// `Foo`, the export macro automatically implements the Upcast trait for `Foo`,
712/// `&Foo`, and `&mut Foo`. This allows these types to be used as return types
713/// for exported functions.
714///
715/// ```
716/// # use ad_astra::{
717/// #     export,
718/// #     runtime::{Cell, Origin},
719/// # };
720/// #
721/// # #[export(include)]
722/// # #[export(package)]
723/// # #[derive(Default)]
724/// # struct Package;
725/// #
726/// #[export]
727/// #[derive(Clone)]
728/// pub struct Foo;
729///
730/// // Cell::give requires that the data parameter implements Upcast.
731/// let _ = Cell::give(Origin::nil(), Foo).unwrap();
732///
733/// #[export]
734/// impl Foo {
735///     // Foo implements Upcast, so you can return Foo.
736///     fn exported_method_1(self) -> Foo { self }
737///
738///     // &Foo implements Upcast, so you can return &Foo.
739///     fn exported_method_2(&self) -> &Foo { self }
740///
741///     // &mut Foo implements Upcast, so you can return &mut Foo.
742///     fn exported_method_3(&mut self) -> &mut Foo { self }
743/// }
744/// ```
745///
746/// ## Manual Implementation
747///
748/// To manually implement the Upcast trait for an exported Rust structure, you
749/// should export a type alias for this structure instead of the structure
750/// itself. The macro system does not automatically implement Upcast for type
751/// aliases, allowing for manual implementation.
752///
753/// Generally, the Upcast trait can be manually implemented for any Rust type,
754/// not necessarily an exported type. This flexibility enables custom Rust
755/// representations of Script data. For example, the Ad Astra crate implements
756/// Upcast for the [Option] container, even though Option is not a [ScriptType].
757///
758/// When upcasting an Option to Cell, the underlying implementation returns
759/// [Cell::nil] if the Option is [None]; otherwise, it unwraps the inner object
760/// and upcasts it.
761///
762/// ```
763/// # use ad_astra::{
764/// #     export,
765/// #     runtime::{Cell, Origin},
766/// # };
767/// #
768/// # #[export(include)]
769/// # #[export(package)]
770/// # #[derive(Default)]
771/// # struct Package;
772/// #
773/// let cell = Cell::give(Origin::nil(), Some(100usize)).unwrap();
774///
775/// assert_eq!(cell.take::<usize>(Origin::nil()).unwrap(), 100);
776///
777/// let cell = Cell::give(Origin::nil(), Some(Some(100usize))).unwrap();
778///
779/// assert_eq!(cell.take::<usize>(Origin::nil()).unwrap(), 100);
780///
781/// let cell = Cell::give(Origin::nil(), Option::<usize>::None).unwrap();
782///
783/// assert!(cell.is_nil());
784///
785/// #[export]
786/// fn exported_function_1() -> Option<usize> { Some(100) }
787///
788/// #[export]
789/// fn exported_function_2() -> Option<Option<usize>> { Some(Some(100)) }
790/// ```
791///
792/// When manually implementing the Upcast trait, you must specify an
793/// [Upcast::Output] associated type that denotes the result of the upcast.
794/// This type is limited to a certain set of possible types. To upcast the type
795/// to a script-registered type `T`, set the Output to `Box<T>`, and create a
796/// box containing the input data (or a transformed version of it) within the
797/// upcast implementation, thereby transferring the data to the heap.
798///
799/// ```
800/// # use ad_astra::{
801/// #     export,
802/// #     runtime::{Cell, Origin, RuntimeResult, ScriptType, TypeHint, Upcast},
803/// # };
804/// #
805/// #[derive(Debug, PartialEq)]
806/// struct Foo;
807///
808/// // Exporting a type alias instead of the struct disables automatic
809/// // implementations of Upcast for this struct, allowing for manual trait
810/// // implementation.
811/// #[export]
812/// type FooAlias = Foo;
813///
814/// impl<'a> Upcast<'a> for Foo {
815///     type Output = Box<Foo>;
816///
817///     fn upcast(_origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
818///         Ok(Box::new(this))
819///     }
820///
821///     fn hint() -> TypeHint {
822///         Foo::type_meta().into()
823///     }
824/// }
825///
826/// let cell = Cell::give(Origin::nil(), Foo).unwrap();
827///
828/// assert_eq!(cell.take::<Foo>(Origin::nil()).unwrap(), Foo);
829/// ```
830///
831/// ## Compositions
832///
833/// When implementing the Upcast trait for a generic container, such as
834/// `Wrapper`, you can require that the generic parameter also implements
835/// Upcast, and then upcast the generic parameter within the container's Upcast
836/// implementation.
837///
838/// ```
839/// # use ad_astra::runtime::{Cell, Origin, RuntimeResult, TypeHint, Upcast};
840/// #
841/// struct Wrapper<T>(T);
842///
843/// impl<'a, T> Upcast<'a> for Wrapper<T>
844/// where
845///     T: Upcast<'a>,
846/// {
847///     type Output = <T as Upcast<'a>>::Output;
848///
849///     #[inline(always)]
850///     fn upcast(origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
851///         <T as Upcast<'a>>::upcast(origin, this.0)
852///     }
853///
854///     #[inline(always)]
855///     fn hint() -> TypeHint {
856///         <T as Upcast<'a>>::hint()
857///     }
858/// }
859///
860/// let cell = Cell::give(Origin::nil(), Wrapper(100usize)).unwrap();
861///
862/// assert_eq!(cell.take::<usize>(Origin::nil()).unwrap(), 100);
863/// ```
864///
865/// This approach allows compositions of Upcast types to also be Upcast:
866/// `Wrapper<T>`, `Wrapper<Option<T>>`, `Option<Wrapper<T>>`, and other
867/// possible combinations are all Upcast types.
868///
869/// ## Lifetime
870///
871/// The Upcast trait has a lifetime parameter `'a`. This parameter indicates
872/// the lifetime of the input type and the output Cell data.
873///
874/// If the target type is an owned type with the `'static` lifetime, this
875/// generic parameter is not significant for the implementation. Typically, the
876/// implementation will simply wrap the input value in a Box and return it.
877///
878/// However, if the Upcast trait is implemented for `&'a T` or similar
879/// referential types or their wrappers, this lifetime must be included in the
880/// [Upcast::Output] type specification.
881///
882/// ```
883/// # use ad_astra::{
884/// #     export,
885/// #     runtime::{Cell, Origin, RuntimeResult, ScriptType, TypeHint, Upcast},
886/// # };
887/// #
888/// #[derive(Debug, PartialEq)]
889/// struct Foo;
890///
891/// #[export]
892/// type FooAlias = Foo;
893///
894/// impl<'a> Upcast<'a> for Foo {
895///     type Output = Box<Foo>;
896///
897///     #[inline(always)]
898///     fn upcast(_origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
899///         Ok(Box::new(this))
900///     }
901///
902///     #[inline(always)]
903///     fn hint() -> TypeHint {
904///         Foo::type_meta().into()
905///     }
906/// }
907///
908/// impl<'a> Upcast<'a> for &'a Foo {
909///     type Output = &'a Foo;
910///
911///     #[inline(always)]
912///     fn upcast(_origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
913///         Ok(this)
914///     }
915///
916///     #[inline(always)]
917///     fn hint() -> TypeHint {
918///         Foo::type_meta().into()
919///     }
920/// }
921///
922/// let cell = Cell::give(Origin::nil(), Foo).unwrap();
923///
924/// // The `Cell::map_ref` function requires that the result type implement
925/// // Upcast on the functor's returned reference.
926/// let mut mapped_cell = cell.map_ref::<Foo>(Origin::nil(), ref_to_ref).unwrap();
927///
928/// assert_eq!(mapped_cell.borrow_ref::<Foo>(Origin::nil()).unwrap(), &Foo);
929///
930/// fn ref_to_ref(foo: &Foo) -> RuntimeResult<&Foo> {
931///     Ok(foo)
932/// }
933/// ```
934///
935/// Note that if you provide an implementation for `&T` or `&mut T`, the
936/// Upcast trait will automatically be implemented for all possible combinations
937/// of references, such as `&&T`, `&&mut T`, etc. This makes Upcast
938/// referentially transparent out of the box, similar to Rust's referential
939/// transparency.
940pub trait Upcast<'a>: Sized + 'a {
941    /// A type into which the input type will be upcasted.
942    ///
943    /// This upcasted type is limited to one of the following options:
944    ///
945    /// - `()`: Corresponds to the [Nil Cell](Cell::nil). Use this type if the
946    ///   upcast implementation always returns a unit value `()`.
947    ///
948    /// - `Box<T>`: Where `T` is a [script-registered type](ScriptType).
949    ///   Use this option to create a Cell with exactly one owned element with
950    ///   `'static` lifetime.
951    ///
952    /// - `Vec<T>`: Where `T` is a script-registered type. Use this option to
953    ///   create a Cell with an array of owned elements with `'static` lifetime.
954    ///
955    /// - `&'a T`, `&'a mut T`, `&'a [T]`, `'a mut [T]`: Where `T` is a
956    ///   script-registered type. Use these options to create a Cell that serves
957    ///   as a projection of another memory allocation.
958    ///
959    /// - `&'a str`: Use this option if the Cell should be a projection of a
960    ///   Unicode string.
961    ///
962    /// - `String`: Use this option if the Cell should own a Unicode string.
963    ///
964    /// - `Cell`: Use this option to manually construct the Cell inside the
965    ///   upcast function implementation.
966    ///
967    /// - [Either<A, B>](Either): Use this option if the resulting Cell could be
968    ///   of type `A` or `B`, where `A` and `B` are any of the upcasting options
969    ///   listed above (including other Either types). To return Cell data from
970    ///   the upcast function, use the [Either::Left] or [Either::Right]
971    ///   variants corresponding to `A` and `B`, respectively.
972    type Output: Upcasted + 'a;
973
974    /// Creates Script data from Rust data.
975    ///
976    /// The `origin` parameter specifies the source code range (either Rust or
977    /// Script) where the data object was requested for creation.
978    ///
979    /// The `this` parameter is the original data object that is to be upcasted
980    /// into the resulting [Cell].
981    ///
982    /// The function returns a [RuntimeError] if the underlying implementation
983    /// is unable to perform the upcasting of the Rust object.
984    fn upcast(origin: Origin, this: Self) -> RuntimeResult<Self::Output>;
985
986    /// Returns a rough estimation of the Script type (or a set of types) into
987    /// which the source Rust type could be upcasted.
988    ///
989    /// The underlying implementation makes the best effort to provide as
990    /// precise type information as possible to support the static semantic
991    /// analysis of the script code. However, the resulting [TypeHint] object
992    /// may be imprecise, up to the [TypeHint::dynamic] value, which indicates
993    /// that the source type(s) is not known at compile time.
994    fn hint() -> TypeHint;
995}
996
997impl<'a, T> Upcast<'a> for &'a &'a T
998where
999    &'a T: Upcast<'a>,
1000{
1001    type Output = <&'a T as Upcast<'a>>::Output;
1002
1003    #[inline(always)]
1004    fn upcast(origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
1005        <&'a T as Upcast>::upcast(origin, this)
1006    }
1007
1008    #[inline(always)]
1009    fn hint() -> TypeHint {
1010        <&'a T as Upcast>::hint()
1011    }
1012}
1013
1014impl<'a, T> Upcast<'a> for &'a &'a mut T
1015where
1016    &'a T: Upcast<'a>,
1017{
1018    type Output = <&'a T as Upcast<'a>>::Output;
1019
1020    #[inline(always)]
1021    fn upcast(origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
1022        <&'a T as Upcast>::upcast(origin, this)
1023    }
1024
1025    #[inline(always)]
1026    fn hint() -> TypeHint {
1027        <&'a T as Upcast>::hint()
1028    }
1029}
1030
1031impl<'a, T> Upcast<'a> for &'a mut &'a T
1032where
1033    &'a T: Upcast<'a>,
1034{
1035    type Output = <&'a T as Upcast<'a>>::Output;
1036
1037    #[inline(always)]
1038    fn upcast(origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
1039        <&'a T as Upcast>::upcast(origin, this)
1040    }
1041
1042    #[inline(always)]
1043    fn hint() -> TypeHint {
1044        <&'a T as Upcast>::hint()
1045    }
1046}
1047
1048impl<'a, T> Upcast<'a> for &'a mut &'a mut T
1049where
1050    &'a mut T: Upcast<'a>,
1051{
1052    type Output = <&'a mut T as Upcast<'a>>::Output;
1053
1054    #[inline(always)]
1055    fn upcast(origin: Origin, this: Self) -> RuntimeResult<Self::Output> {
1056        <&'a mut T as Upcast>::upcast(origin, this)
1057    }
1058
1059    #[inline(always)]
1060    fn hint() -> TypeHint {
1061        <&'a mut T as Upcast>::hint()
1062    }
1063}
1064
1065pub trait Upcasted {
1066    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain>;
1067}
1068
1069impl Upcasted for () {
1070    #[inline(always)]
1071    fn into_chain(self, _origin: Origin) -> RuntimeResult<UpcastedChain> {
1072        Ok(UpcastedChain::Cell(Cell::nil()))
1073    }
1074}
1075
1076impl Upcasted for Cell {
1077    #[inline(always)]
1078    fn into_chain(self, _origin: Origin) -> RuntimeResult<UpcastedChain> {
1079        Ok(UpcastedChain::Cell(self))
1080    }
1081}
1082
1083impl Upcasted for String {
1084    #[inline(always)]
1085    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain> {
1086        Ok(UpcastedChain::Slice(MemorySlice::register_string(
1087            origin, self,
1088        )?))
1089    }
1090}
1091
1092impl<T: ScriptType> Upcasted for Box<T> {
1093    #[inline(always)]
1094    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain> {
1095        if TypeId::of::<T>() == TypeId::of::<()>() {
1096            return Ok(UpcastedChain::Cell(Cell::nil()));
1097        }
1098
1099        let this = Box::into_raw(self);
1100
1101        // Safety: Vector data originated from Box data that represents owned singleton slice.
1102        let vector = unsafe { Vec::from_raw_parts(this, 1, 1) };
1103
1104        Ok(UpcastedChain::Slice(MemorySlice::register_vec(
1105            origin, vector,
1106        )?))
1107    }
1108}
1109
1110impl<T: ScriptType> Upcasted for Vec<T> {
1111    #[inline(always)]
1112    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain> {
1113        if TypeId::of::<T>() == TypeId::of::<()>() {
1114            return Ok(UpcastedChain::Cell(Cell::nil()));
1115        }
1116
1117        Ok(UpcastedChain::Slice(MemorySlice::register_vec(
1118            origin, self,
1119        )?))
1120    }
1121}
1122
1123impl<'a, T: ScriptType> Upcasted for &'a T {
1124    #[inline(always)]
1125    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain> {
1126        if TypeId::of::<T>() == TypeId::of::<()>() {
1127            return Ok(UpcastedChain::Cell(Cell::nil()));
1128        }
1129
1130        // Safety: Transparent layout transmutation.
1131        let slice = unsafe { transmute::<&T, &[T; 1]>(self) } as &[T];
1132
1133        Ok(UpcastedChain::Slice(MemorySlice::register_slice_ref(
1134            origin, slice,
1135        )?))
1136    }
1137}
1138
1139impl<'a, T: ScriptType> Upcasted for &'a mut T {
1140    #[inline(always)]
1141    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain> {
1142        if TypeId::of::<T>() == TypeId::of::<()>() {
1143            return Ok(UpcastedChain::Cell(Cell::nil()));
1144        }
1145
1146        // Safety: Transparent layout transmutation.
1147        let slice = unsafe { transmute::<&mut T, &mut [T; 1]>(self) } as &mut [T];
1148
1149        Ok(UpcastedChain::Slice(MemorySlice::register_slice_mut(
1150            origin, slice,
1151        )?))
1152    }
1153}
1154
1155impl<'a> Upcasted for &'a str {
1156    #[inline(always)]
1157    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain> {
1158        Ok(UpcastedChain::Slice(MemorySlice::register_str(
1159            origin, self,
1160        )?))
1161    }
1162}
1163
1164impl<'a, T: ScriptType> Upcasted for &'a [T] {
1165    #[inline(always)]
1166    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain> {
1167        if TypeId::of::<T>() == TypeId::of::<()>() {
1168            return Ok(UpcastedChain::Cell(Cell::nil()));
1169        }
1170
1171        Ok(UpcastedChain::Slice(MemorySlice::register_slice_ref(
1172            origin, self,
1173        )?))
1174    }
1175}
1176
1177impl<'a, T: ScriptType> Upcasted for &'a mut [T] {
1178    #[inline(always)]
1179    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain> {
1180        if TypeId::of::<T>() == TypeId::of::<()>() {
1181            return Ok(UpcastedChain::Cell(Cell::nil()));
1182        }
1183
1184        Ok(UpcastedChain::Slice(MemorySlice::register_slice_mut(
1185            origin, self,
1186        )?))
1187    }
1188}
1189
1190/// An alternative between two upcasted types.
1191///
1192/// See [Upcast::Output] for details.
1193pub enum Either<L: Upcasted, R: Upcasted> {
1194    /// The first alternative of the upcasted type.
1195    Left(L),
1196
1197    /// The second alternative of the upcasted type.
1198    Right(R),
1199}
1200
1201impl<L: Upcasted, R: Upcasted> Upcasted for Either<L, R> {
1202    #[inline(always)]
1203    fn into_chain(self, origin: Origin) -> RuntimeResult<UpcastedChain> {
1204        match self {
1205            Self::Left(left) => left.into_chain(origin),
1206            Self::Right(right) => right.into_chain(origin),
1207        }
1208    }
1209}
1210
1211pub enum UpcastedChain {
1212    Slice(Arc<MemorySlice>),
1213    Cell(Cell),
1214}