on ca435e8 help sign in
dodrio/src/render.rs
145 lines 4.0 kB history raw
1use crate::Node;
2use bumpalo::Bump;
3use std::any::Any;
4use std::rc::Rc;
5use wasm_bindgen::UnwrapThrowExt;
7/// A trait for any component that can be rendered to HTML.
8///
9/// Takes a shared reference to `self` and generates the virtual DOM that
10/// represents its rendered HTML.
11///
12/// ## `Bump` Allocation
13///
14/// `Render` implementations can use the provided `Bump` for very fast
15/// allocation for anything that needs to be allocated during rendering.
16///
17/// ## The `'a: 'bump` Lifetime Bound
18///
19/// The `'a: 'bump` bounds enforce that `self` outlives the given bump
20/// allocator. This means that if `self` contains a string, the string does not
21/// need to be copied into the output `Node` and can be used by reference
22/// instead (i.e. it prevents accidentally using the string after its been
23/// freed). The `'a: 'bump` bound also enables abstractions like
24/// `dodrio::Cached` that can re-use cached `Node`s across `render`s without
25/// copying them.
26///
27/// ## Example
28///
29/// ```no_run
30/// use dodrio::{bumpalo::Bump, Node, Render};
31///
32/// pub struct MyComponent;
33///
34/// impl Render for MyComponent {
35/// fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
36/// where
37/// 'a: 'bump
38/// {
39/// Node::text("This is my component rendered!")
40/// }
41/// }
42/// ```
43pub trait Render {
44 /// Render `self` as a virtual DOM. Use the given `Bump` for temporary
45 /// allocations.
46 fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
47 where
48 'a: 'bump;
51impl<'r, R> Render for &'r R
52where
53 R: Render,
55 fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
56 where
57 'a: 'bump,
58 {
59 (**self).render(bump)
60 }
63impl<R> Render for Rc<R>
64where
65 R: Render,
67 fn render<'a, 'bump>(&'a self, bump: &'bump Bump) -> Node<'bump>
68 where
69 'a: 'bump,
70 {
71 (**self).render(bump)
72 }
75/// A `RootRender` is a render component that can be the root rendering component
76/// mounted to a virtual DOM.
77///
78/// In addition to rendering, it must also be `'static` so that it can be owned
79/// by the virtual DOM and `Any` so that it can be downcast to its concrete type
80/// by event listener callbacks.
81///
82/// You do not need to implement this trait by hand: there is a blanket
83/// implementation for all `Render` types that fulfill the `RootRender`
84/// requirements.
85pub trait RootRender: Any + Render {
86 /// Get this `&RootRender` trait object as an `&Any` trait object reference.
87 fn as_any(&self) -> &Any;
89 /// Get this `&mut RootRender` trait object as an `&mut Any` trait object
90 /// reference.
91 fn as_any_mut(&mut self) -> &mut Any;
94impl<T: Any + Render> RootRender for T {
95 fn as_any(&self) -> &Any {
96 self
97 }
99 fn as_any_mut(&mut self) -> &mut Any {
100 self
101 }
104impl dyn RootRender {
105 /// Downcast this shared `&dyn RootRender` trait object reference to its
106 /// underlying concrete type.
107 ///
108 /// # Panics
109 ///
110 /// Panics if this virtual DOM's root rendering component is not an `R`
111 /// instance.
112 pub fn unwrap_ref<R: RootRender>(&self) -> &R {
113 self.as_any()
114 .downcast_ref::<R>()
115 .expect_throw("bad `RootRender::unwrap_ref` call")
116 }
118 /// Downcast this exclusive `&mut dyn RootRender` trait object reference to
119 /// its underlying concrete type.
120 ///
121 /// # Panics
122 ///
123 /// Panics if this virtual DOM's root rendering component is not an `R`
124 /// instance.
125 pub fn unwrap_mut<R: RootRender>(&mut self) -> &mut R {
126 self.as_any_mut()
127 .downcast_mut::<R>()
128 .expect_throw("bad `RootRender::unwrap_ref` call")
129 }
132#[cfg(test)]
133mod tests {
134 #[test]
135 fn render_is_object_safe() {
136 #[allow(dead_code)]
137 fn takes_dyn_render(_: &dyn super::Render) {}
138 }
140 #[test]
141 fn root_render_is_object_safe() {
142 #[allow(dead_code)]
143 fn takes_dyn_render(_: &dyn super::RootRender) {}
144 }