-
Notifications
You must be signed in to change notification settings - Fork 98
Description
Situation: I want to map the JS DOM Node, Element, HTMLElement, etc. classes to WIT somehow so that I can import them from the JS host in my Rust WASM Component. These DOM classes have an inheritance hierarchy. I am unsure how to best map these concepts to WIT resource types.
yes i could just use wasm_bindgen ecosystem but this is more hypothetical experiment-y than practical i-need-this-for-a-business-project
Option 1: T::cast_from_super(u) and t.cast_to_super() owned to owned conversions
- avoids duplicating methods for each class in the chain
- lots of consuming conversions to go back and forth
- the most barebones of the bunch; also the most cross-wasm func calls
- i dont think WIT can do methods that consume (not borrow) the self param so these would be static methods i guess
// document.querySelector() returns Element | null
let element = document.query_selector("input[name=username]").unwrap();
let html_element = HtmlElement::cast_from_super(element).unwrap();
let html_input_element = HtmlInputElement::cast_from_super(html_element).unwrap();
// use the <input> tag with things like .value or .reportValidity()
html_input_element.get_value();
html_input_element.report_validity();
// cast back to Element to use Element.prototype.id
let html_element = html_input_element.cast_to_super();
let element = html_element.cast_to_super();
element.get_id();
element.set_id("username");Option 2: T::cast_from_super(u) and t.cast_to_super() owned to owned conversions but with inherited methods duplicated on extended classes
- lots of duplicating base class methods for all classes that extend it
- less cast_to_super, still walk the cast_from_super chain
- EACH METHOD on EACH CHILD TYPE which can be A LOT
// document.querySelector() returns Element | null
let element = document.query_selector("input[name=username]").unwrap();
let html_element = HtmlElement::cast_from_super(element).unwrap();
let html_input_element = HtmlInputElement::cast_from_super(html_element).unwrap();
// use the <input> tag with things like .value or .reportValidity()
html_input_element.get_value();
html_input_element.report_validity();
// DON'T cast back to Element to use Element.prototype.id
html_input_element.get_id();
html_input_element.set_id("username");Option 3: T::from_U(&) and (&t).as_U() borrowed to owned-copy methods
- two owned resources for the same underlying value -- can't return
borrow<T>iirc? - can skip to the end of the inheritance chain in one function instead of walking it manually
// document.querySelector() returns Element | null
let element = document.query_selector("input[name=username]").unwrap();
let html_input_element = HtmlInputElement::from_element(element).unwrap(); // see also from_html_element() and from_node()
// use the <input> tag with things like .value or .reportValidity()
html_input_element.get_value();
html_input_element.report_validity();
// reuse element to use Element.prototype.id
element.get_id();
// or create another owned copy by downcasting
let element2 = html_input_element.as_element();
element2.set_id("username");
do_something_with_element(element); // takes owned Element instance, not HtmlInputElement
// somehow we still have our own owned HtmlInputElement instance AND element2Option 4: T::from_U(&) and (&t).as_U() borrowed to owned-copy methods but with inherited methods duplicated on all extending classes
- still two owned resources
- basically only from_U() unless need to make T into U for a method argument
- EACH METHOD on EACH CHILD TYPE which can be A LOT
// document.querySelector() returns Element | null
let element = document.query_selector("input[name=username]").unwrap();
let html_input_element = HtmlInputElement::from_element(element).unwrap(); // see also from_html_element() and from_node()
// element is still owned too!
// use the <input> tag with things like .value or .reportValidity()
html_input_element.get_value();
html_input_element.report_validity();
// use Element.prototype.id dup'd on all classes that extend Element
html_input_element.get_id();
html_input_element.set_id("username");
do_something_with_element(element); // takes owned Element instance, not HtmlInputElement
// somehow we still have our own owned HtmlInputElement instanceOption 5: T::from_U(u) and t.into_U() owned to owned conversion methods for ALL parent class types
- quicker shortcuts than option 1
- still have to cast back and forth for inherited methods
// document.querySelector() returns Element | null
let element = document.query_selector("input[name=username]").unwrap();
let html_input_element = HtmlInputElement::from_element(element).unwrap(); // see also from_html_element() and from_node()
// use the <input> tag with things like .value or .reportValidity()
html_input_element.get_value();
html_input_element.report_validity();
// cast back to Element to use Element.prototype.id
let element = html_input_element.into_element(); // see also into_html_element() and into_node()
element.get_id();
element.set_id("username");Option 6: T::from_U(u) and t.into_U() owned to owned conversion methods for ALL parent class types
- most ergonomic i think; also minimal cross-wasm func calls
- have to dup for each conversion from_T() and into_T()
- EACH METHOD on EACH CHILD TYPE which can be A LOT
// document.querySelector() returns Element | null
let element = document.query_selector("input[name=username]").unwrap();
let html_input_element = HtmlInputElement::from_element(element).unwrap(); // see also from_html_element() and from_node()
// use the <input> tag with things like .value or .reportValidity()
html_input_element.get_value();
html_input_element.report_validity();
// DON'T cast back to Element to use Element.prototype.id
html_input_element.get_id();
html_input_element.set_id("username");
do_something_with_element(html_input_element.into_element()); // see also into_html_element() and into_node()Option 7? are there other ways im missing?
i havent manually tested making bindings and running each of these rust code options in a js runtime that provides these import to it (too much effort). im looking for some feedback on how to model the general "class T extends U" scenario in WIT. this is just an example scenario that im having right now. Which one of these best fits the WIT vibes? are the "shortcut"-style methods encouraged in WIT (since they incur an extra across-component-boundary func call) or is that so fast nowadays that it shouldnt matter? what about if its worth the extra bindings size to duplicate the entire HTMLElement prototype attribute list (60+ getter/setter methods) for EVERY SINGLE HTML<something>Element subclass -- should that be done in the guest language (impl in Rust instead of in WIT)?
looking for more opinions