1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//! See [`EmulatedImports`] for documentation.
use std::sync::Arc;
use std::{ffi, mem};
use crate::dom::element::DomElement;
use crate::ComponentImports;
/// Used to emulate the imports that a component calls.
///
/// One use case is for testing components.
/// During tests a component might use the [`ComponentImports`] from
/// [`EmulatedImports::component_imports`], whereas in when running in the Afia runtime it would
/// use [`ComponentImports::new_dynamically_linked`].
pub struct EmulatedImports {
emulated_imports_ptr: Arc<EmulatedImportsPtr>,
}
pub(crate) struct EmulatedImportsPtr(*mut std::ffi::c_void);
impl Drop for EmulatedImportsPtr {
fn drop(&mut self) {
unsafe { afia_component_sys::free_emulated_imports(self.0) }
}
}
impl EmulatedImports {
/// Create a new `EmulatedImports`.
pub fn new() -> Self {
let imports = unsafe { afia_component_sys::new_emulated_imports() };
Self {
emulated_imports_ptr: Arc::new(EmulatedImportsPtr(imports)),
}
}
/// Create a [`ComponentImports`] that uses this `EmulatedImports`.
///
/// After the `EmulatedImports` instance has been dropped it is undefined behavior to use this
/// [`ComponentImports`] instance, or any objects that were creating from this
/// `ComponentImports` instance.
pub fn component_imports(&self) -> ComponentImports {
ComponentImports {
component_imports_ptr: self.emulated_imports_ptr(),
created_via_emulated_imports: Some(self.emulated_imports_ptr.clone()),
}
}
/// Get the DOM as a string.
///
/// TODO: Delete this and instead create an `__afia__$dom$outer_html` method that a component
/// can use to retrieve an element's outer HTML.
/// Benefit is that users' test and implementation code will be able to use `outer_html`,
/// whereas [`EmulatedImports::outer_html`] is meant to be used during tests and is not designed
/// to be used by components that are running in a real website.
/// In summary, add an `ImportKind::OuterHtml` and `crates/site-component-tests mod dom` test
/// case for `outer_html`, then delete this [`EmulatedImports::outer_html`].
pub fn outer_html(&self, element: DomElement) -> String {
let outer_html = unsafe {
afia_component_sys::emulated_imports_dom_outer_html(
self.emulated_imports_ptr(),
element.to_i64(),
)
};
let outer_html_str = std::str::from_utf8(unsafe {
std::slice::from_raw_parts(outer_html.pointer, outer_html.len)
})
.unwrap();
let outer_html_string = outer_html_str.to_string();
unsafe { afia_component_sys::free_allocated_string(outer_html) };
outer_html_string
}
/// After using [`DomElement::add_event_listener_with_callback`] to add an event listener, the event handler
/// will call this function that is passed into [`EmulatedImports::set_handle_events`].
pub fn set_handle_events<T>(
&self,
handle_event: extern "C" fn(event_handle: i64, ctx: *mut T),
) {
// SAFETY: TODO think through whether this cast is safe.
let handle_event: extern "C" fn(event_handle: i64, ctx: *mut ffi::c_void) =
unsafe { mem::transmute(handle_event) };
unsafe {
afia_component_sys::emulated_imports_set_event_handler(
self.emulated_imports_ptr(),
handle_event,
)
}
}
fn emulated_imports_ptr(&self) -> *mut std::ffi::c_void {
self.emulated_imports_ptr.0
}
}