Working on a minimal prototype/test program for Dom manipulation using Rust and wasm-bindgen
.
Building the element with document.create_element(tag)
, which returns a web_sys::Element
. The element is casted to specific type, depending on tag e.g. web_sys::HtmlButtonElement
. This works and furthermore put in a small wrapper used to setup element type specific builders.
struct ElemSpec<T> {
html_tag : html_const::HtmlTag<'static>,
resource_type: std::marker::PhantomData<T>
}
impl<T : wasm_bindgen::JsCast> ElemSpec<T> {
fn build_element(&self) -> T {
let window = web_sys::window().expect("no global `window` exists");
let document = window.document().expect("should have a document on window");
let elem = document.create_element(&self.html_tag);
let elem = match elem {
Ok(e) => e,
Err(error) => {
panic!("Call to document.create_element failed with error: {:?}", error)
},
};
let result = wasm_bindgen::JsCast::dyn_into::<T>(elem);
let helement = match result {
Ok(e) => e,
Err(e) => {
panic!("Cast to specific Element failed tag:{}", &self.html_tag);
},
};
helement
}
}
const BUTTON_ELEM : ElemSpec<web_sys::HtmlButtonElement> =
ElemSpec{ html_tag: html_const::BUTTON, resource_type: std::marker::PhantomData};
This works, an HtmlButtonElement
is built with:
let buttonElement = BUTTON_ELEM.build_element();
Now I look for a trait Bound, that restricts to elements, which could be casted from web_sys::Element
. E.g. HtmlSpanElement
, HtmlDivElement
, ..., HtmlButtonElement
.
An additional or replacement Bound to wasm_bindgen::JsCast
in impl<T : wasm_bindgen::JsCast>
, could this be done?
The wasm_bindgen::JsCast::dyn_into
documentations states it relies on JsCast::has_type
, which in turn calls JsCast::instanceof
. And indeed web_sys::Element
does implement JsCast
trait. So do the other HTML element types such as HtmlButtonElement
.
The implementation is generated from Web IDL files, which are the formal definition of web browser interfaces. However:
impl JsCast for Element {
fn instanceof(val: &JsValue) -> bool {
#[link(wasm_import_module = "__wbindgen_placeholder__")]
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
extern "C" {
fn __widl_instanceof_Element(val: u32) -> u32;
}
#[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))]
unsafe fn __widl_instanceof_Element(_: u32) -> u32 {
panic!("cannot check instanceof on non-wasm targets");
}
unsafe {
let idx = val.into_abi();
__widl_instanceof_Element(idx) != 0
}
}
}
the generated instanceof
method calls into WebIDL native library. Since it crosses the language boundary, it can't tell us much about what these element types have in common on the Rust side.
On the other hand, HtmlButtonElement
and others also implement AsRef<Element>
as:
impl AsRef<Element> for HtmlButtonElement {
#[inline]
fn as_ref(&self) -> &Element {
use wasm_bindgen::JsCast;
Element::unchecked_from_js_ref(self.as_ref())
}
}
So there you have it, one common bound among them is AsRef<Element>
. Most likely this is a conscious design decision by web-sys
authors because it also makes sense from WebIDL / JavaScript perspective: