afia_component/context.rs
1//! Some functions in the site component wasm ABI pass context to the Afia host.
2//!
3//! The Afia host will then pass the context back when it calls the site component's exported
4//! functions.
5//!
6//! ```
7//! // TODO: This `afia-component` crate will eventually abstract over these `extern`
8//! // functions. When that happens we'll want to update this example to use the abstraction.
9//! // Users of this crate won't be working directly with the low-level C ABI, they'll be using
10//! // higher level abstractions that this crate exposes.
11//!
12//! use afia_component::ComponentImports;
13//!
14//! #[export_name = "__afia__$create_instance"]
15//! pub extern "C" fn __afia_create_instance() -> isize {
16//! 123
17//! }
18//!
19//! #[export_name = "__afia__$output$123$create_element"]
20//! pub extern "C" fn create_element(context: isize, _inputs_ptr: isize, _inputs_len: usize) -> i64 {
21//! assert_eq!(context, 123);
22//!
23//! let div = ComponentImports::new_dynamically_linked().create_element("div").unwrap();
24//! div.temporary_way_to_get_i64()
25//! }
26//! ```
27//!
28//! This module contains types that are useful when passing and receiving context from the Afia host.
29
30use crate::context::crate_private::CratePrivate;
31
32/// Some `afia-component` functions have a `context` parameter.
33///
34/// This `context` gets passed to Afia, and then Afia later passes the context back to the site
35/// component.
36///
37/// Types that implement this `Context` trait can be used as `context`.
38pub trait Context: sealed::Sealed {
39 /// Get an `isize` representation of the `Context` .
40 fn to_isize(&self, _: CratePrivate) -> isize;
41}
42mod sealed {
43 pub trait Sealed {}
44}
45pub(crate) mod crate_private {
46 pub struct CratePrivate;
47}
48
49impl Context for isize {
50 fn to_isize(&self, _: CratePrivate) -> isize {
51 *self
52 }
53}
54impl sealed::Sealed for isize {}
55
56impl<T> Context for *const T {
57 fn to_isize(&self, _: CratePrivate) -> isize {
58 *self as isize
59 }
60}
61impl<T> sealed::Sealed for *const T {}
62
63impl<T> Context for *mut T {
64 fn to_isize(&self, _: CratePrivate) -> isize {
65 *self as isize
66 }
67}
68impl<T> sealed::Sealed for *mut T {}
69
70/// Provides a safe abstraction over context that is a mutable pointer.
71///
72/// TODO: Not sure whether the `afia-component` will eventually abstract this away such that
73/// the user does not need to use it... Still feeling out this `site-componenet-utils` crate...
74#[repr(C)]
75// rustc regression is making this trigger a dead code warning even though it shouldn't.
76// can remove this `#[allow(dead_code)]` once the regression is fixed
77// https://github.com/rust-lang/rust/issues/126706
78#[allow(dead_code)]
79pub struct ContextPtr<T>(*mut T);
80
81/// Wraps a context pointer such that it can be passed to the Afia host, but it cannot be accessed
82/// by the site component.
83///
84/// This is useful when the component is already holding an `&mut T` reference and wishes to avoid
85/// unintentionally creating two mutable references.
86///
87/// TODO: Not sure whether the `afia-component` will eventually abstract this away such that
88/// the user does not need to use it... so... not including an example for now.
89#[repr(C)]
90// rustc regression is making this trigger a dead code warning even though it shouldn't.
91// can remove this `#[allow(dead_code)]` once the regression is fixed
92// https://github.com/rust-lang/rust/issues/126706
93#[allow(dead_code)]
94pub struct OpaqueContextPtr<T>(*mut T);
95
96impl<T> ContextPtr<T> {
97 /// Get a mutable reference to the value pointed to by a context pointer.
98 ///
99 /// ```
100 /// // TODO: This `afia-component` crate will eventually abstract over these `extern`
101 /// // functions. When that happens we'll want to update this example to use the abstraction.
102 /// // Users of this crate won't be working directly with the low-level C ABI, they'll be using
103 /// // higher level abstractions that this crate exposes.
104 ///
105 /// use afia_component::ComponentImports;
106 /// use afia_component::context::ContextPtr;
107 ///
108 /// struct Context {
109 /// imports: ComponentImports,
110 /// label: &'static str
111 /// }
112 ///
113 /// #[export_name = "__afia__$create_instance"]
114 /// pub extern "C" fn create_instance() -> *mut Context {
115 /// Context::new_raw_with_imports_label(ComponentImports::new_dynamically_linked(), "world")
116 /// }
117 ///
118 /// #[export_name = "__afia__$output$123$create_element"]
119 /// pub extern "C" fn create_element(mut context: ContextPtr<Context>, _inputs_ptr: isize, _inputs_len: usize) -> i64 {
120 /// let div = context.with_ref_mut(|ctx, _| {
121 /// assert_eq!(ctx.label, "world");
122 ///
123 /// let div = ctx.imports.create_element("div").unwrap();
124 /// div.set_attribute("hello", ctx.label);
125 ///
126 /// div
127 /// });
128 /// div.temporary_way_to_get_i64()
129 /// }
130 ///
131 /// impl Context {
132 /// fn new_raw_with_imports_label(imports: ComponentImports, label: &'static str) -> *mut Context {
133 /// Box::into_raw(Box::new(Context { imports, label }))
134 /// }
135 /// }
136 /// ```
137 pub fn with_ref_mut<Ret>(
138 &mut self,
139 run: impl FnOnce(&mut T, OpaqueContextPtr<T>) -> Ret,
140 ) -> Ret {
141 let val = unsafe { &mut *self.0 };
142 let opaque = OpaqueContextPtr(self.0);
143 run(val, opaque)
144 }
145}
146
147impl<T> Context for OpaqueContextPtr<T> {
148 fn to_isize(&self, _: CratePrivate) -> isize {
149 self.0 as isize
150 }
151}
152impl<T> sealed::Sealed for OpaqueContextPtr<T> {}
153
154impl<T> OpaqueContextPtr<T> {
155 /// Get an `i32` that can get sent from the component to the Afia host.
156 ///
157 /// TODO: Make the `afia-component` methods that call the Afia host take this
158 /// [`OpaqueContextPointer`] as an argument and then we can use [`Context::to_isize`]
159 /// to convert it to `isize` from within `afia-component`.
160 /// Then we can delete this `to_i32` method.
161 /// This way the site component can't get access to the underlying value, making this
162 /// a truly opaque wrapper.
163 pub fn to_i32(&self) -> i32 {
164 self.0 as i32
165 }
166}