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}