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}