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    /// let (emulated, imports) = ComponentImports::new_emulated();
70    /// let div = imports.create_element("div").unwrap();
71    /// div.set_attribute("class", "main-info");
72    /// assert_eq!(
73    ///     emulated.outer_html(div),
74    ///     r#"<div class="main-info"></div>"#
75    /// )
76    /// ```
77    ///
78    /// [`EmulatedImports`]: emulated::EmulatedImports
79    #[cfg(feature = "emulate-imports")]
80    pub fn new_emulated() -> (emulated::EmulatedImports, Self) {
81        let emulated = emulated::EmulatedImports::new();
82        let imports = emulated.component_imports();
83
84        (emulated, imports)
85    }
86
87    /// Get the pointer to the component imports.
88    pub(crate) fn pointer(&self) -> *const std::ffi::c_void {
89        self.component_imports_ptr
90    }
91}
92
93/// Holds the arguments passed to an `__afia__$output$123` function.
94#[derive(Clone)]
95pub struct ComponentOutputArgs {
96    /// The component instance's context, represented as a type-erased pointer.
97    pub ctx: *mut (),
98    /// The [`ComponentImports`].
99    pub imports: ComponentImports,
100    /// Before running the component, the Afia host copies the component's inputs into the
101    /// component guest's memory.
102    /// This pointer points to those inputs.
103    pub inputs_ptr: *const u8,
104}
105
106/// Represents a type that can be created from [`ComponentOutputArgs`].
107pub trait FromComponentOutputArgs {
108    /// Create the type using [`ComponentOutputArgs`].
109    fn from_args(args: ComponentOutputArgs) -> Self;
110}
111
112impl FromComponentOutputArgs for ComponentImports {
113    fn from_args(args: ComponentOutputArgs) -> Self {
114        args.imports.clone()
115    }
116}