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}