Skip to main content

afia_component/
lib.rs

1#![deny(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4mod attribute;
5pub mod callback;
6pub mod context;
7pub mod data_element_value;
8pub mod dom;
9mod event_listener;
10pub mod input;
11pub mod local_storage;
12pub mod node_property;
13pub mod stripe;
14
15mod output_change;
16
17#[cfg(feature = "macros")]
18pub use afia_component_macro::*;
19
20#[doc(hidden)]
21pub mod _macro;
22
23#[cfg(feature = "emulate-imports")]
24pub mod emulated;
25
26/// A handle that can be used to call component imports such as [`ComponentImports::create_element`]
27/// and [`ComponentImports::create_text`].
28///
29/// [`EmulatedImports`]: emulated::EmulatedImports
30///
31/// ## ComponentImports and `Clone`
32/// Cloning a `ComponentImports` is cheap and does not allocate.
33#[derive(Clone)]
34pub struct ComponentImports {
35    component_imports_ptr: *const std::ffi::c_void,
36    /// If the `ComponentImports` were created via [`emulated::EmulatedImports::component_imports`],
37    /// then the `component_imports_ptr` will become invalid when the emulated imports are freed.
38    ///
39    /// This field ensures that the emulated imports will never be freed before this
40    /// `ComponentImports`, meaning that the `component_imports_ptr` will always point to a valid
41    /// [`emulated::EmulatedImports`].
42    #[expect(
43        unused,
44        reason = "Only here to ensure that the emulated imports don't get dropped."
45    )]
46    #[cfg(feature = "emulate-imports")]
47    created_via_emulated_imports: Option<std::sync::Arc<emulated::EmulatedImportsPtr>>,
48}
49impl ComponentImports {
50    /// Create a new `ComponentImports` that makes calls to functions that are provided by the
51    /// Afia host.
52    ///
53    /// Use [`EmulatedImports::component_imports`] to create `ComponentImports` during tests.
54    ///
55    /// [`EmulatedImports`]: emulated::EmulatedImports
56    pub const fn new_dynamically_linked() -> Self {
57        Self {
58            component_imports_ptr: std::ptr::null(),
59            #[cfg(feature = "emulate-imports")]
60            created_via_emulated_imports: None,
61        }
62    }
63
64    /// Create a new `ComponentImports` that makes calls to emulated functions that are provided by
65    /// an [`EmulatedImports`] instance.
66    ///
67    /// ## Examples
68    /// ```
69    /// # use afia_component::ComponentImports;
70    ///
71    /// let (emulated, imports) = ComponentImports::new_emulated();
72    /// let div = imports.create_element("div").unwrap();
73    /// div.set_attribute("class", "main-info");
74    /// assert_eq!(
75    ///     emulated.outer_html(div),
76    ///     r#"<div class="main-info"></div>"#
77    /// )
78    /// ```
79    ///
80    /// [`EmulatedImports`]: emulated::EmulatedImports
81    #[cfg(feature = "emulate-imports")]
82    pub fn new_emulated() -> (emulated::EmulatedImports, Self) {
83        let emulated = emulated::EmulatedImports::new();
84        let imports = emulated.component_imports();
85
86        (emulated, imports)
87    }
88
89    /// Get the pointer to the component imports.
90    pub(crate) fn pointer(&self) -> *const std::ffi::c_void {
91        self.component_imports_ptr
92    }
93}
94
95/// Holds the arguments passed to an `__afia__$output$123` function.
96#[derive(Clone)]
97pub struct ComponentOutputArgs {
98    /// The component instance's context, represented as a type-erased pointer.
99    pub ctx: *mut (),
100    /// The [`ComponentImports`].
101    pub imports: ComponentImports,
102    /// Before running the component, the Afia host copies the component's inputs into the
103    /// component guest's memory.
104    /// This pointer points to those inputs.
105    pub inputs_ptr: *const u8,
106}
107
108/// Represents a type that can be created from [`ComponentOutputArgs`].
109pub trait FromComponentOutputArgs {
110    /// Create the type using [`ComponentOutputArgs`].
111    fn from_args(args: ComponentOutputArgs) -> Self;
112}
113
114impl FromComponentOutputArgs for ComponentImports {
115    fn from_args(args: ComponentOutputArgs) -> Self {
116        args.imports.clone()
117    }
118}